这是我参与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