iOS使用block需注意循环应用的问题
1,一个对象内互相引用
比如TBViewController2 对象对全局属性block引用,而block内部又引用了TBViewController2 自身对象造成循环引用,内存得不到释放
如下代码(Xcode会发出警告, 比较容易发现)
self.myBlock = ^() {
[self doSomething];
};
myBlock();
又如,Xcode不会放出警告
void(^myBlock)() = ^() {
[self doSomething];
};
self.myBlock = myBlock;
myBlock();
解决方案,关键字__weak 修饰self,使block不再持有self
__weak typeof (self) weakSelf = self;
self.myBlock = ^() {
[weakSelf doSomething];
};
self.myBlock();
2,多个对象互相引用
TBViewController2 类实例对象属性持有TBBlockObject对象,TBBlockObject内部属性block又持有了TBViewController2对象,也会造成相互引用,内存得不到释放,而这错种错误编译器很难发现,不会发处警告。
代码如下:
TBBlockObject *tbObject = [[TBBlockObject alloc] init];
self.tbObject = tbObject;
tbObject.myBlock = ^{
[self doSomething];
};
解决方案: __weak 修饰TBViewController2 self对象,使block不再持有TBViewController2 self对象:
TBBlockObject *tbObject = [[TBBlockObject alloc] init];
self.tbObject = tbObject;
__weak typeof (self) weakSelf = self;
tbObject.myBlock = ^{
[weakSelf doSomething];
};
3,绝大部分GCD都不会产生循环引用
因为对象(TBViewController2)本身并没有对block进行持有,所以不会产生循环引用。
// CGD 绝大部分都不会产生循环引用
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});
下列代码会产生循环引用
// 如果对对象本身对 block持有,也会出现循环引用
void(^myBlock)() = ^{
[self doSomething];
};
self.myBlock = myBlock;
dispatch_async(dispatch_get_main_queue(), myBlock);
解决方案,原理同上,利用__weak不让对象block持有对象本生
// 如果对对象本身对 block持有,使用weakSelf
__weak typeof (self) weakSelf = self;
void(^myBlock)() = ^{
[weakSelf doSomething];
};
self.myBlock = myBlock;
dispatch_async(dispatch_get_main_queue(), myBlock);
4,2个对象拥有同一个block
假设TBViewController2对象和TBBlockObject同时拥有同一个block, 而block里面持有对象TBViewController2,但是某个时刻TBViewController2释放了,就会导致在运行TBBlockObject的block的时候出错。
TBBlockObject *tbBlockObject = [[TBBlockObject alloc] init];
self.tbBlockObject = tbBlockObject;
TBViewController2 *vc2 = [[TBViewController2 alloc] init];
// 注意weak,否则会循环引用
__weak typeof(vc2) weakVC2 = vc2;
void(^myBlock)() = ^() {
[weakVC2 doSomething];
};
// 赋值block
vc2.myBlock = myBlock;
tbBlockObject.myBlock = myBlock;
// 执行
vc2.myBlock();
tbBlockObject.myBlock();
[self.navigationController pushViewController:vc2 animated:YES];
当退出vc2的时候,再执行
[self.tbBlockObject excuteTBBlockObject];
就会出错了(无输出日志)