在使用Block時常趁6啵可以看到Weak-Strong Dance
的用法, 很多的文章以及官方文檔都舉例了這樣做的原因. 但是還尚未發(fā)現(xiàn)有對strong進行講解的. 下面就舉個栗子具體分析下為什么加strong以及何時起作用
首先放上兩個類似 ReactiveCocoa 中 定義weakify和strongify的宏 以便下文用到
#define WeakObj(o) autoreleasepool{} __weak typeof(o) weak##o = o
#define StrongObj(o) autoreleasepool{} __strong typeof(o) o = weak##o
一党觅、weak的作用(代碼+注解 簡單跳過)
防止被block捕獲(會導(dǎo)致引用計數(shù)加1), 打破循環(huán)引用(retain cycle)
// DeallocMonitor繼承NSObject, 僅重寫其dealloc方法, 并在其中打印其被釋放日志
DeallocMonitor *object1 = [DeallocMonitor new];
DeallocMonitor *object2 = [DeallocMonitor new];
@WeakObj(object2);//__weak typeof(object2) weakobject2 = object2;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
// object1被block捕獲, 引用計數(shù)加1, 外部作用域結(jié)束時仍未被釋放, 直至該Block執(zhí)行完畢才被釋放
NSLog(@"5s已到, %@該被釋放勒", object1);
// weakobject2被weak 修飾, 其指向的object2對象的引用計數(shù)不會增加, 當(dāng)外部作用域結(jié)束時就已被釋放
NSLog(@"5s已到, %@早已被釋放, 此處為null", weakobject2);
});
// 外部作用域結(jié)束
二、為何要加strong, 其何時才起作用?
加strong的原因想必大家都知道是為了防止block執(zhí)行過程中 __weak typeof(object) weakObject
指向的對象突然被釋放了, 這就會導(dǎo)致block中的代碼運行結(jié)果出現(xiàn)意想不到的結(jié)果(比如一些代碼執(zhí)行有效, 其余代碼執(zhí)行無效; 弱引用的對象因為為nil而導(dǎo)致的crash等.)
2.1 即使加了strong, 也不能保證weakObject指向的對象不會被釋放
只能確保在block執(zhí)行期間, weakObject指向的對象有效(不會被釋放)
下面這段代碼就是在block中用strong申明的對象強引用一次weakObject, 但修飾對象在block執(zhí)行前就已經(jīng)被釋放的栗子
// DeallocMonitor繼承NSObject, 僅重寫其dealloc方法, 并在其中打印其被釋放日志
DeallocMonitor *object = [DeallocMonitor new];
@WeakObj(object);// weakobject
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
// 該strongObj的申明僅在block執(zhí)行時才見效, 而外部作用域一結(jié)束object就已經(jīng)被釋放了, 所以然并卵
@StrongObj(object);
/* weakobject用 weak修飾, 故其引用計數(shù)不變,
上邊的宏本意是申明一個新的object局域變量對weakobject指向的原object進行強引用..
按理 原object引用計數(shù)應(yīng)該會加1, 可是它還沒等到被強引用時就已經(jīng)掛掉了
*/
NSLog(@"5s已到, %@然后早已被釋放, 此處為null", object);
});
// 外部作用域結(jié)束
2.2 Block內(nèi)部申明的強引用指針變量指向weakObject僅在block執(zhí)行時才有效
定義該Block的時strongObj宏還尚未使原對象引用計數(shù)加1! 那么strongObj宏生效時的表現(xiàn)是什么樣子的呢? 繼續(xù)上代碼
// 該段代碼主要是打了一個時間差, 以模擬strong申明起作用的情形
// DeallocMonitor繼承NSObject, 僅重寫其dealloc方法, 并在其中打印其被釋放日志
DeallocMonitor *object = [DeallocMonitor new];
// 保證外部作用域結(jié)束的2.5秒(無限接近..)內(nèi)object不會被釋放
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.5 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
NSLog(@"果斷強引用object: %@\n 還能再多堅持2.5s", object);
});
@WeakObj(object);// weakobject
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
@StrongObj(object);// __strong typeof(object) object = weakobject
sleep(3);// 卡個3s
// 此處就不會像上一段代碼那樣, 強引用一個為nil的object, 故weakobject指向的對象引用計數(shù)加1, 直到該block運行完, 才會被釋放
NSLog(@"5s已到, %@打印完這個日志就飛升了", object);
});
// 外部作用域結(jié)束
2.3 有多少個嵌套block就應(yīng)該申明多少對weak-strong
假定我們在最外層block使用的一對weak-strong, 且外層block內(nèi)還有一個block(沒有用weak-strong)引用到了strongObj宏申明的局域變量object, 并假設(shè)原對象在外層block開始運行前一直存活, 這就會導(dǎo)致內(nèi)層block捕獲到局域變量object并使其指向?qū)ο蟮囊糜嫈?shù)加1, 因為內(nèi)層block捕獲到了外層block中申明的object(強引用), 就跟外層block會捕獲到外部強引用變量指向的對象一樣一樣的
DeallocMonitor *object = [DeallocMonitor new];
@WeakObj(object);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
@StrongObj(object);// 因為block運行時, weakObject指向?qū)ο笠琅f存在, 故該強引用使其引用計數(shù)加1
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
// 這一層block 發(fā)現(xiàn)上邊的object是強引用, 導(dǎo)致捕獲到其指向?qū)ο? 使其引用計數(shù)在該內(nèi)層block尚未執(zhí)行時就加1了
NSLog(@"打印完這個日志, %@才被釋放", object);
});
NSLog(@"%@外層block結(jié)束, 引用計數(shù)減一", object);
});
sleep(3);
// 外部作用域所在線程小歇一會, 確保object存活3s, 作用域結(jié)束
所以嵌套block時 萬萬要小心, 不要漏寫了. 另外weak-strong要成對出現(xiàn), 不然少一個strong, 都有可能為此付出代價
2.4 遺漏補缺
- 在block中對外部weakObject進行強引用(strong修飾)的結(jié)果是使weakObject指向的原對象的引用計數(shù)加1, 因為weakObject指針指向的是原對象在堆中的存儲地址
- block 不會對弱引用指針變量指向的對象進行捕獲
2.5 block的相關(guān)知識, 個人推薦書籍章節(jié)
- Effective-ObjectiveC(Item 37: Understand Blocks)
- Pro Multithreading and Memory Management for iOS and OS X(Blocks Implementation)
三开财、題外篇(內(nèi)存泄露檢測工具-媽媽再也不用擔(dān)心內(nèi)存泄露)
對于ReactiveCocoa以及各種嵌套Block的常用玩家..想必僅靠Xcode的Instrument去檢測memory leak問題是絕對不夠的, 個人賣瓜推薦一個檢測內(nèi)存泄露的小工具類:
FXDeallocMonitor
拷貝FXDeallocMonitor.h芦疏、FXDeallocMonitor.m文件到項目中, 根據(jù)頭文件中的方法調(diào)用就行, 簡單易用??
對于需求更高者, 推薦近期facebook開源的FBAllocationTracker