assign vs weak
assign適用于基本數(shù)據(jù)類型鳖藕,weak是適用于NSObject對象,并且是一個弱引用。
assign其實也可以用來修飾對象,那么我們?yōu)槭裁床挥盟夭龋恳驗楸籥ssign修飾的對象在釋放之后,指針的地址還是存在的霍殴,也就是說指針并沒有被置為nil媒惕。如果在后續(xù)的內(nèi)存分配中,剛好分到了這塊地址来庭,程序就會崩潰掉妒蔚。
而weak修飾的對象在釋放之后,指針地址會被置為nil。
__block vs __weak
__block:使用__block修飾的變量可以在__block中被修改肴盏,且會被retain(MRC下不會retain)
__weak:使用__weak修飾的變量不會在block代碼塊中被retain
同時科盛,在ARC下,要避免block出現(xiàn)循環(huán)引用要用:
__weak__typeof(&*self)weakSelf =self;
等同于__weakUIViewController*weakSelf =self;
為什么不用block 是因為通過引用來訪問self的實例變量 菜皂,self被retain,block也是一個強引用贞绵,引起循環(huán)引用,用week是弱引用幌墓,當(dāng)self釋放時但壮,weakSelf已經(jīng)等于nil。
MRC模式下
使用__block能夠避免引起循環(huán)引用的問題
ARC模式下
使用__unsafe_unretained?和 __weak都可以避免循環(huán)引用的問題常侣,但由于前者是unsafe的蜡饵,會造成野指針問題,所以盡量少用unsafe_unretained關(guān)鍵字
另外在多線程環(huán)境下(block中的wSelf有可能被析構(gòu)的情況下)胳施,需要先將self轉(zhuǎn)為strong指針溯祸,避免在運行到某個關(guān)鍵步驟時self對象被析構(gòu)。
可參考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內(nèi)部對self強引用舞肆,weak一下焦辅。
其中用到了__typeof(self),這里涉及幾個知識點:
1. __typeof椿胯、__typeof__筷登、typeof的區(qū)別
他們沒有區(qū)別,在早期C語言中沒有typeof這個關(guān)鍵字哩盲,__typeof前方、__typeof__是在C語言的擴展關(guān)鍵字的時候出現(xiàn)的。
typeof是現(xiàn)代GNU C++的關(guān)鍵字廉油,從Objective-C的根源說惠险,他其實來自于C語言,所以AFNetworking使用了繼承自C的關(guān)鍵字抒线。
2. 對于老的LLVM編譯器上面這句話會編譯報錯班巩,所以在很早的ARC使用者中流行__typeof(&*self)這種寫法,原因如下
大致說法是老LLVM編譯器會將__typeof轉(zhuǎn)義為 XXX類名 *const __strong的__strong和前面的__weak關(guān)鍵字對指針的修飾又沖突了嘶炭,所以加上&*對指針的修飾抱慌。
第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
self轉(zhuǎn)回strong了,這里__typeof()里面寫的是weakSelf旱物,里面寫self也沒有問題遥缕,因為typeof是編譯時確定變量類型,所以這里寫self 不會被循環(huán)引用宵呛。
第四单匣、五、六行,如果不轉(zhuǎn)成strongSelf而使用weakSelf户秤,后面幾句話中码秉,有可能在第四句執(zhí)行之后self的對象可能被析構(gòu)掉,然后后面的StausBlock沒有執(zhí)行鸡号,導(dǎo)致邏輯錯誤转砖。
最后第五行,使用前對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];
當(dāng)有 retain block 時,應(yīng)該使用 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 會有危險不铆,因為在多執(zhí)行緒下,weakSelf 有可能在 block 跑到一半的時候被設(shè)成 nil
__weak typeof(self) weakSelf = self;
dispatch_block_t block =? ^{
? ? [weakSelf doSomething]; // weakSelf != nil
? ? // preemption, weakSelf turned nil
? ? [weakSelf doSomethingElse]; // weakSelf == nil
};
因此必須在 block 內(nèi)使用 strongSelf裹唆,確保 reference 不會執(zhí)行到一半變成 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;
? ? }
};
總結(jié):
1. 當(dāng) block 不是 property 時誓斥,用 self 即可
2. 當(dāng) block 是 property,需使用 weakSelf
3. 當(dāng) block 內(nèi)會多次使用 weakSelf许帐,且有用到多執(zhí)行緒劳坑,需使用 strongSelf
4. 並不是所有 block 都得用 weakSelf?(事實上大多數(shù)的 iOS 原生套件,以及 GCD 的 block 是不會造成 retain cycle 的成畦,因為他們並沒有去 retain block)
此外也可以藉由把 block property 設(shè)為 nil 來打破 retain cycle
例如 AFNetworking 就使用了類似的實作方式
因此在 AFNetworking 的 block 中使用 self 也不會造成 retain cycle 問題
另外即使將變數(shù)直接宣告成 instance variable 而非 property泡垃,在 block 中使用時還是會 retain 到 self 而發(fā)生 retain cycle,因為 ivar 其實也是 self 的一部分
@interface MyViewController () {
? ? NSString *tempStr;
}
self.completionHandler = ^{
? ? // 這裡的 tempStr 相當(dāng)於 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);
}