总之这一切其实就是为了防止循环引用,下面结合网络上各位大牛的分析精华分析分析
block对于其变量都会形成强引用(retain),对于self也会形成强引用(retain) ,而如果self本身对block也是强引用的话,就会形成 强引用 循环,无法释放——造成内存泄露
什么是循环引用:对象A持有对象B,对象B持有对象A,相互持有,最终导致两个对象都不能释放。
循环引用场景:
1、block在主函数体用到了self / self.变量 / [self 方法],意味着:block对self 进行持有操作;
2、self声明了属性变量block,block用copy来修饰,意味着:self对block进行持有操作,会造成循环引用;
例如下图,现在这样写Xcode直接就会有警告出来告诉你这里貌似retain cycle了啊!!
循环引用例子
假如我想在block里面改变局部变量的值或者使用局部变量,如下图这么写Xcode直接报错 而且给出了解决方法提示出来了 这点还挺机智的,那么我们使用__block修饰之后,再次在block里面使用,就不会出问题了,完美。
解决
:
使用局部变量.png
在使用block的地方:
__block typeof(self) bself = self; // 适用MRC模式,
__block NSString *tempStr = @"abc";
__block int a = 0; //当修饰变量时,表示这个变量值能在block中被修改
__weak typeof(self) weakself = self; // 适用ARC模式
关于——Block在MRC和ARC模式的区别
1)__block在MRC下有两个作用
允许在Block中访问和修改局部变量
禁止Block对所引用的对象进行隐式retain操作
2)__block在ARC下只有一个作用
允许在Block中访问和修改局部变量
什么时候在 block 中不需要使用 weakSelf???
- 大部分GCD方法,因为self并没有对GCD的block进行持有,没有形成循环引用。目前我还没碰到使用GCD导致循环引用的场景,如果某种场景self对GCD的block进行了持有,则才有可能造成循环引用。
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});
- 大部分动画效果,当动画结束时,UIView 会结束持有这个 block,block 对象就会释放掉,从而 block 会释放掉对于 self 的持有。整个内存引用关系被解除。
[UIView animateWithDuration:0.2 animations:^{
self.alpha = 1;
}];
- block并不是对象的属性 / 变量,而是方法的参数 / 临时变量,这里因为block只是一个临时变量,self并没有对其持有,所以没有造成循环引用
.- (void) testWithBlock:(void(^)())block {
block();
}
-
还有一种情况,block属于Person对象,在另一个类里面使用的时候没有构成互相持有,这时候也不需要weakSelf。
-
使用GCD的时候,这么写其实是错误的,并不是见到block就要为了防止循环引用而使用weakSelf,像下面这种写法,就是错误的:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf doSomething];
});
因为将block作为参数传给dispatch_async时,系统会将block拷贝到堆上,而且block会持有block中用到的对象,因为dispatch_async并不知道block中对象会在什么时候被释放,为了确保系统调度执行block中的任务时其对象没有被意外释放掉,dispatch_async必须自己retain一次对象(即self),任务完成后再release对象(即self)。但这里使用__weak,使dispatch_async没有增加self的引用计数,这使得在系统在调度执行block之前,self可能已被销毁,但系统并不知道这个情况,导致block执行时访问已经被释放的self,而达不到预期的结果。
总结
1 在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。
2 如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。
网友评论