iOS之Block报错:capturing 'self' strongly in this block is likely to lead to a retain cycle
2016-04-21 11:51
603 查看
我们经常在block使用中碰到:capturing 'self' strongly in this block is likely to lead to a retain cycle
重要法则:
block对于其变量都会形成强引用(retain),对于self也会形成强引用(retain),而如果self本身对block也是强引用的话,就会形成强引用循环,无法释放——造成内存泄露。
1)self会对自己的属性或函数进行持有。
2)block主函数体,用到了self所拥有的任何东西(self、self.属性、[self
函数] ),block就会对self进行持有。
3)如果1、2情况同时存在,就会出现retain cycle问题。因为1很难做出改变,所以解决方法是对2进行改进。
如何避免?
1、简单判断:我一般重写dealloc方法,查看dealloc方法是否调用来判断是否有循环引用。
2、深层分析:根据上面法则来分析代码。
1、被block引用的变量都会被自动retain一次,这样的话至少可以保证我们的调用是有效的。因为block中的retain是隐式的,所以极易出现retain
cycle的问题。
2、retain cycle,翻译成中文大概叫保留环吧。比如A和B两个对象,A持有B,B同时也持有A,A只有B释放之后才有可能释放,同样B只有A释放后才可能释放。当双方都在等待对方释放的时候,
retain cycle就形成了。结果是,两个对象都永远不会被释放,最终内存泄露。
3、__strong: 赋值给这个变量的对象会自动被retain一次,如果在block中引用它,block也会retain它一次。
4、__unsafe_unretained: 赋值给这个变量不会被retain,也就是说被他修饰的变量的存在不能保证持有对象的可靠性,它可能已经被释放了,而且留下了一个不安全的指针。不会被block retain。
5、__weak:类似于__unsafe_unretained,只是如果所持有的对象被释放后,变量会自动被设置为nil,这样更安全些,不过只在IOS5.0以上的系统支持,同样不会被block retain。
6、__block: 表示这个变量值能在block中被修改(值修改,而不是修改对象中的某一个属性,可以理解为修改指针的指向)。会被自动retain。
被 __block 修饰的变量在块中保存的是变量的地址。(其他为变量的值)
使用__weak打破循环的方法只在ARC下才有效,在MRC下应该使用__block
先用代码描述一下症状:
[objc] view
plain copy
/* ViewController.h */
#import <UIKit/UIKit.h>
typedef void (^ABlock)(void); //定义一个简单的Block
@interface ViewController : UIViewController {
// self的属性
NSMutableArray *_items;
// 1、self的属性,换句话说,self持有block,即self对block具有强引用
ABlock _block;
}
@end
/* ViewController.m */
#import "ViewController.h"
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_items = [[NSMutableArray alloc] init];
_block = ^{
// 2、_block内部用到了self的属性_items,换句话说,_block持有了self,导致retain cycle。
// 因为只要 block中用到了对象的属性或者函数,block就会持有该对象而不是该对象中的某个属性或者函数。
[_items addObject:@"Hello!"];
};
}
@end
Xcode在编译以上程序的时候会给出一个警告:Captureing ‘self’ strongly in this block is likely to lead to a retain cycle。
原因:_items实际上是self->items。_block对象在创建的时候会被retain一次,因此会导致self也被retain一次。这样就形成了一个retain cycle。
解决方法:创建一个本地变量blockSelf,指向self,然后用结构体语法访问实例变量。代码如下:
[objc] view
plain copy
__block ViewController *blockSelf = self;
_block = ^{
[blockSelf->_items addObject:@"Hello!"];
};
这么修改之后,blockSelf是本地变量,是弱引用,因此在_block被retain的时候,并不会增加retain count,所以retain cycle就解除了,Xcode也不再出现警告了,问题解决。
例子2:
[objc] view
plain copy
DoSomethingManager *manager = [[DoSomethingManager alloc] init];
manager.complete = ^{
[manager otherAction];
[manager release];
};
retain cycle 就这么形成了,即使调用了release,manager也不会释放,因为manager和block相互持有了。为了解除retain
cycle的话,我们可以这样写:
[objc] view
plain copy
__block DoSomethingManager *manager = [[DoSomethingManager alloc] init];
__weak DoSomethingManager *weakmanager = manager;
manager.complete = ^{
[weakmanager otherAction];
};
重要法则:
block对于其变量都会形成强引用(retain),对于self也会形成强引用(retain),而如果self本身对block也是强引用的话,就会形成强引用循环,无法释放——造成内存泄露。
1)self会对自己的属性或函数进行持有。
2)block主函数体,用到了self所拥有的任何东西(self、self.属性、[self
函数] ),block就会对self进行持有。
3)如果1、2情况同时存在,就会出现retain cycle问题。因为1很难做出改变,所以解决方法是对2进行改进。
如何避免?
1、简单判断:我一般重写dealloc方法,查看dealloc方法是否调用来判断是否有循环引用。
2、深层分析:根据上面法则来分析代码。
1、被block引用的变量都会被自动retain一次,这样的话至少可以保证我们的调用是有效的。因为block中的retain是隐式的,所以极易出现retain
cycle的问题。
2、retain cycle,翻译成中文大概叫保留环吧。比如A和B两个对象,A持有B,B同时也持有A,A只有B释放之后才有可能释放,同样B只有A释放后才可能释放。当双方都在等待对方释放的时候,
retain cycle就形成了。结果是,两个对象都永远不会被释放,最终内存泄露。
3、__strong: 赋值给这个变量的对象会自动被retain一次,如果在block中引用它,block也会retain它一次。
4、__unsafe_unretained: 赋值给这个变量不会被retain,也就是说被他修饰的变量的存在不能保证持有对象的可靠性,它可能已经被释放了,而且留下了一个不安全的指针。不会被block retain。
5、__weak:类似于__unsafe_unretained,只是如果所持有的对象被释放后,变量会自动被设置为nil,这样更安全些,不过只在IOS5.0以上的系统支持,同样不会被block retain。
6、__block: 表示这个变量值能在block中被修改(值修改,而不是修改对象中的某一个属性,可以理解为修改指针的指向)。会被自动retain。
被 __block 修饰的变量在块中保存的是变量的地址。(其他为变量的值)
使用__weak打破循环的方法只在ARC下才有效,在MRC下应该使用__block
先用代码描述一下症状:
[objc] view
plain copy
/* ViewController.h */
#import <UIKit/UIKit.h>
typedef void (^ABlock)(void); //定义一个简单的Block
@interface ViewController : UIViewController {
// self的属性
NSMutableArray *_items;
// 1、self的属性,换句话说,self持有block,即self对block具有强引用
ABlock _block;
}
@end
/* ViewController.m */
#import "ViewController.h"
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_items = [[NSMutableArray alloc] init];
_block = ^{
// 2、_block内部用到了self的属性_items,换句话说,_block持有了self,导致retain cycle。
// 因为只要 block中用到了对象的属性或者函数,block就会持有该对象而不是该对象中的某个属性或者函数。
[_items addObject:@"Hello!"];
};
}
@end
Xcode在编译以上程序的时候会给出一个警告:Captureing ‘self’ strongly in this block is likely to lead to a retain cycle。
原因:_items实际上是self->items。_block对象在创建的时候会被retain一次,因此会导致self也被retain一次。这样就形成了一个retain cycle。
解决方法:创建一个本地变量blockSelf,指向self,然后用结构体语法访问实例变量。代码如下:
[objc] view
plain copy
__block ViewController *blockSelf = self;
_block = ^{
[blockSelf->_items addObject:@"Hello!"];
};
这么修改之后,blockSelf是本地变量,是弱引用,因此在_block被retain的时候,并不会增加retain count,所以retain cycle就解除了,Xcode也不再出现警告了,问题解决。
例子2:
[objc] view
plain copy
DoSomethingManager *manager = [[DoSomethingManager alloc] init];
manager.complete = ^{
[manager otherAction];
[manager release];
};
retain cycle 就这么形成了,即使调用了release,manager也不会释放,因为manager和block相互持有了。为了解除retain
cycle的话,我们可以这样写:
[objc] view
plain copy
__block DoSomethingManager *manager = [[DoSomethingManager alloc] init];
__weak DoSomethingManager *weakmanager = manager;
manager.complete = ^{
[weakmanager otherAction];
};
相关文章推荐
- iOS 签发证书无效问题详解
- ios 解决 dyld: Library not loaded: /System/Library/Frameworks/CoreMedia.framework/CoreMedia
- iOS navgationBar 的颜色相关设置
- iOS 开发小技巧
- iOS调试
- iOS到Mac OS之NSTableView的简单使用
- iOS 子视图加入到父视图及移除父视图的过程
- [IOS/第三方工具]CocoaPods 终端上功能以及使用说明
- ios按钮长按事件
- AFNetWorking的get与post用法以及进行图片上传
- 如何控制百度地图的显示范围 ios软件开发
- ios按钮滑动切换
- 开发笔记-27个iOS开发中的小技巧
- iOS调整系统屏幕亮度
- iOS开发经验总结
- IOS9获取HTML数据报错解决
- [BS-02] iOS数组、字典、NSNumber 新写法—— @[]、@{}
- iOS设置NavigationBar颜色和BackButtonItem颜色以及navigation的应用
- IOS7 iBeacons探寻
- 多线程 - dispatch_barrier_async 使用及解析