assign vs weak
assign适用于基本数据类型,weak是适用于NSObject对象,并且是一个弱引用。
assign其实也可以用来修饰对象,那么我们为什么不用它呢?因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。
而weak修饰的对象在释放之后,指针地址会被置为nil。
__block vs __weak
__block:使用__block修饰的变量可以在__block中被修改,且会被retain(MRC下不会retain)
__weak:使用__weak修饰的变量不会在block代码块中被retain
同时,在ARC下,要避免block出现循环引用要用:
__weak__typeof(&*self)weakSelf =self;
等同于__weakUIViewController*weakSelf =self;
为什么不用block 是因为通过引用来访问self的实例变量 ,self被retain,block也是一个强引用,引起循环引用,用week是弱引用,当self释放时,weakSelf已经等于nil。
MRC模式下
使用__block能够避免引起循环引用的问题
ARC模式下
使用__unsafe_unretained 和 __weak都可以避免循环引用的问题,但由于前者是unsafe的,会造成野指针问题,所以尽量少用unsafe_unretained关键字
另外在多线程环境下(block中的wSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。
可参考AFNetworking代码:
__weak__typeof(self)weakSelf =self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { __strong__typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus= status;
if(strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}};
第一行:__weak __typeof(self)weakSelf = self;
为防止callback内部对self强引用,weak一下。
其中用到了__typeof(self),这里涉及几个知识点:
1. __typeof、__typeof__、typeof的区别
他们没有区别,在早期C语言中没有typeof这个关键字,__typeof、__typeof__是在C语言的扩展关键字的时候出现的。
typeof是现代GNU C++的关键字,从Objective-C的根源说,他其实来自于C语言,所以AFNetworking使用了继承自C的关键字。
2. 对于老的LLVM编译器上面这句话会编译报错,所以在很早的ARC使用者中流行__typeof(&*self)这种写法,原因如下
大致说法是老LLVM编译器会将__typeof转义为 XXX类名 *const __strong的__strong和前面的__weak关键字对指针的修饰又冲突了,所以加上&*对指针的修饰。
第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
self转回strong了,这里__typeof()里面写的是weakSelf,里面写self也没有问题,因为typeof是编译时确定变量类型,所以这里写self 不会被循环引用。
第四、五、六行,如果不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行之后self的对象可能被析构掉,然后后面的StausBlock没有执行,导致逻辑错误。
最后第五行,使用前对block判空。
若 object 本身沒有去 retain 這個 block (即沒有把這個 block 作成一個 property),則可以直接在 block 中使用 self
dispatch_block_t completionBlock = ^{
// 未 retain block,可直接用 self
NSLog(@"%@", self);
}
MyViewController *myController = [[MyViewController alloc] init...];
[self presentViewController:myController animated:YES completion:completionHandler];
若有 retain block,直接用 self 會造成 retain cycle
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
__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;
}
};
總結:
1. 當 block 不是 property 時,用 self 即可
2. 當 block 是 property,需使用 weakSelf
3. 當 block 內會多次使用 weakSelf,且有用到多執行緒,需使用 strongSelf
4. 並不是所有 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);
}