Block 中何時可以直接用 self,何時必須用 weakSe
若 object 本身没有去 retain 这个 block (即没有把这个 block 作成一个 property),则可以直接在 block 中使用 self
//block 不是 property
dispatch_block_t completionBlock = ^{
// 未 retain block,可直接用 self
NSLog(@"%@", self);
}
MyViewController *myController = [[MyViewController alloc] init...];
[self presentViewController:myController
animated:YES
completion:completionHandler];
//把这个 block 作成一个 property
self.completionHandler = ^{
// 有 retain block,直接用 self 会造成 retain cycle
NSLog(@"%@", self);
}
MyViewController *myController = [[MyViewController alloc] init...];
[self presentViewController:myController
animated:YES
completion:self.completionHandler];
当有 retain block 时,应该使用 weakSelf
__weak typeof(self) weakSelf = self;
self.completionHandler = ^{
// 打破 retain cycle
NSLog(@"%@", weakSelf);
};
MyViewController *myController = [[MyViewController alloc] init...];
[self presentViewController:myController
animated:YES
completion:self.completionHandler];
但只用 weakSelf 的问题在于,如果在 block 中必须多次使用到 weakSelf 会有危险,因为在多事件执行时,weakSelf 有可能在 block 跑到一半的时候被设成 nil
__weak typeof(self) weakSelf = self;
dispatch_block_t block = ^{
[weakSelf doSomething]; // weakSelf != nil
// preemption, weakSelf turned nil
[weakSelf doSomethingElse]; // weakSelf == nil
};
因此必须在 block 内使用 strongSelf,确保 reference 不会执行到一半变成 nil (注意在建立 strongSelf 以后还会再判断其是否为 nil,因为有可能在指定 strongSelf 的时间点 weakSelf = self 就已经为 nil 了)
__weak typeof(self) weakSelf = self;
myObj.myBlock = ^{
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomething]; // strongSelf != nil
// preemption, strongSelf still not nil
[strongSelf doSomethingElse]; // strongSelf != nil
}
else {
// Probably nothing...
return;
}
};
总结:
- 当 block 不是 property 时,用 self 即可
- 当 block 是 property,需使用 weakSelf
- 当 block 内会多次使用 weakSelf,且有用到多次执行时,需使用 strongSelf
- 并不是所有 block 都得用 weakSelf
事实上大多数的 iOS 原生套件,以及 GCD 的 block 是不会造成 retain cycle 的,因为他们并没有去 retain block
此外也可以因为把 block property 设为 nil 来打破 retain cycle
例如 AFNetworking 就使用了类似的操作方式
因此在 AFNetworking 的 block 中使用 self 也不会造成 retain cycle 问题
另外即使将变量声明成 instance variable(实例变量) 而非 property(属性),在 block 中使用时还是会 retain 到 self 而发生 retain cycle,因为 ivar 其实也是 self 的一部份
@interface MyViewController () {
NSString *tempStr;
}
self.completionHandler = ^{
// 这里的 tempStr 相当于 self->tempStr,因此还是会造成 retain cycle
NSLog(@"%@", tempStr);
}
但 ivar 又无法使用 weakSelf 去取值,因此解決方法有:
- 1、乖乖建立 property (可能比较简单)
- 2、使用 weakSelf + strongSelf
__weak __typeof(self) weakSelf = self;
self.completionHandler = ^{
// 用 weakSelf->tempStr 是无法取值的
__strong __typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"%@", strongSelf->tempStr);
}
更详细说明参照:
https://github.com/oa414/objc-zen-book-cn#-block
http://blog.waterworld.com.hk/post/block-weakself-strongself