这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战
block类型
GlobalBlock:位于全局区,在block内部不使用外部变量,或者只使用静态变量或者全局变量
MallocBlock:位于堆区,在block内部使用局部变量或者OC属性,并且赋值给强引用或者copy修饰的变量
StackBlock:位于栈区,与堆区的block一样,可以在内部使用局部变量或者OC属性,但是不能赋值给强引用或者copy修饰的变量
以下是上面三种类型的block创建方式
不使用外部变量:
void (^block)(void) = ^{ };
复制代码

使用了局部变量,并且强引用:
int a = 10;
void (^block)(void) = ^{
NSLog(@" - %d",a);
};
复制代码

使用了局部变量,但是没有强引用:
int a = 10;
void (^ __weak block)(void) = ^{
NSLog(@"- %d",a);
};
复制代码

block默认是强引用,指向的是^{}的内存空间。
block捕获外部变量
- (void)blockDemo2{
NSObject *objc = [NSObject new];
NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)(objc))); // 1
void(^strongBlock)(void) = ^{ // 这里实际上是strongBlock对objc捕获 所以这里 +1
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
};
strongBlock();
void(^__weak weakBlock)(void) = ^{ // + 1
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
};
weakBlock();
void(^mallocBlock)(void) = [weakBlock copy];
mallocBlock();
}
复制代码
最终,对外部的objc引用计数是多少呢?

分析:这里的疑问点主要是在strongBlock()这里,按照我们的常识strongBlock对objc捕获所以这里 +1应该是2,那么为什么打印成了3?
原因这里的strongBlock是一个堆区的block,它是从栈上拷贝一份到了堆区,所以这里也要+1,具体的我们下面结合着源码libclosure分析。
所以上面的strongBlock = __weak weakBlock + [weakBlock copy]
block内存拷贝
- (void)blockDemo1{
int a = 0;
void(^ __weak weakBlock)(void) = ^{
NSLog(@"-----%d", a);
};
struct _LGBlock *blc = (__bridge struct _LGBlock *)weakBlock;
// 深浅拷贝
id __strong strongBlock = weakBlock;
blc->invoke = nil;
void(^strongBlock1)(void) = strongBlock;
strongBlock1();
}
复制代码
文中的_LGBlock是一个自定义的block,这里的重点关注的是block的内存拷贝,所以这个细节可以忽略。运行上述代码会崩溃。因为当前的blc->invoke = nil, 也侧面验证了blc和weakBlock指向的是同一片内存空间,所以当blc置为nil的时候,此时崩溃。那么怎么修改呢,其实我们可以这样做。
// 深浅拷贝 此时这里是个堆区block 值跟weakBlock一样,但是是一个全新的内存地址
id __strong strongBlock = [weakBlock copy];
复制代码
block堆栈释放差异
- (void)blockDemo3{
int a = 0;
void(^__weak weakBlock)(void) = nil;
{
void(^__weak strongBlock)(void) = ^{
NSLog(@"---%d", a);
};
weakBlock = strongBlock;
NSLog(@"---1");
}
weakBlock();
}
复制代码

在代码块中栈区strongBlock赋值给了栈区weakBlock。
那如果我们这样修改呢?
- (void)blockDemo3{
int a = 0;
void(^__weak weakBlock)(void) = nil;
{
void(^strongBlock)(void) = ^{
NSLog(@"---%d", a);
};
weakBlock = strongBlock;
NSLog(@"---1");
}
weakBlock();
}
复制代码
此时运行,程序崩溃在了weakBlock()这里。此时的strongBlock是一个堆区的block,堆区的block赋值给weakBlock时候把该Block也变成了堆区的block

但是strongBlock在出了作用域之后释放了,所以此时的weakBlock指向的地址也为空,所以报错。其实主要是要区分堆栈释放的区域。
block拷贝到堆区
- 手动
copy block作为返回值- 被强引用或者
copy修饰 - 系统的API包含
usingBlock
block循环引用
下面的例子由于self -> block -> self所以造成了循环引用。
self.block = ^(void){
NSLog(@"%@", self.name);
};
self.block();
复制代码
解决的办法是加一个__weak:self -> block -> weakSelf(nil)
__weak typeof(self) weakSelf = self;
self.block = ^(void){
NSLog(@"%@",weakSelf.name);
};
self.block();
复制代码

那么我再加一个延时操作呢?
__weak typeof(self) weakSelf = self;
self.block = ^(void){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",weakSelf.name);
});
};
self.block();
复制代码

此时的weakSelf.name已经为空。还没有来得及反应就已经释放了。那这个怎么解决呢?
__weak typeof(self) weakSelf = self;
self.block = ^(void){
__strong __typeof(weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.name);
});
};
self.block();
复制代码

weak``strong合理的应用安排。block内部的strongSelf是一个临时变量,出了作用域就释放。
解决办法二手动释放: self -> block -> self -> block
__block ViewController *vc = self;
self.block = ^(void){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
vc = nil;
});
};
self.block();
复制代码

解决办法三: 引入参数,就不会捕获self
self.block = ^(ViewController * vc){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
});
};
self.block(self);
复制代码

weak相关面试题
以下代码块会造成循环引用嘛?
static ViewController *staticSelf_;
- (void)blockWeak_static {
// 是同一片内存空间
__weak typeof(self) weakSelf = self;
staticSelf_ = weakSelf;
}
复制代码
运行,发现dealloc并没有走,所以此时造成了循环引用。分析:weakSelf和self指向的是同一片内存空间。

此时staticSelf_ = weakSelf后,全局变量持有了self,所以不会释放。
接着,下面的会造成循环引用吗?
- (void)block_weak_strong {
__weak typeof(self) weakSelf = self;
self.doWork = ^{
__strong typeof(self) strongSelf = weakSelf;
weakSelf.doStudent = ^{
NSLog(@"%@", strongSelf);
};
weakSelf.doStudent();
};
self.doWork();
}
复制代码
运行,发现dealloc并没有走,所以此时也造成了循环引用。分析:在NSLog(@"%@", strongSelf);这一行的时候,block对strongSelf进行捕获,此时strongSelf.count+1,所以不会被释放。
解决方式:不引入这个strongSelf























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)