iOS 中block的迴圈引用問題
阿新 • • 發佈:2018-12-21
開發中經常使用weakSelf和strongSelf來解決block的迴圈引用問題,但是是不是所有的block都會導致迴圈引用呢?顯然不是的,那麼怎麼判斷呼叫一個帶有block方法時是否會造成迴圈引用呢,我們來分析一下。
首先我們來寫一個含有block的類,並呼叫自己,然後在外部實現這個block,來測試什麼情況會出現迴圈引用。
@interface ALDTestBlockModel () @property (nonatomic, copy) dispatch_block_t testBlock; @property (nonatomic, copy) NSString * testString; @end @implementation ALDTestBlockModel - (void)initWithBlock1:(dispatch_block_t)block{ //物件不持有該block if (block) { block(); } } - (void)initWithBlock2:(dispatch_block_t)block{ //物件持有該block self.testBlock = block; if (self.testBlock) { self.testBlock(); } } @end
外部測試程式碼:
@interface ALDTestBlockController () @property (nonatomic, strong) ALDTestBlockModel *model; @end @implementation ALDTestBlockController - (void)viewDidLoad { [super viewDidLoad]; self.model = [ALDTestBlockModel new]; [self noSelfTest]; //[self haveSelfTest]; } - (void)noSelfTest{ [self.model initWithBlock1:^{ NSLog(@"1111self=[]"); }]; [self.model initWithBlock2:^{ NSLog(@"2222self=[]"); }]; } - (void)haveSelfTest{ [self.model initWithBlock1:^{ NSLog(@"1111self====== %@",self.testString); }]; [self.model initWithBlock2:^{ NSLog(@"2222self====== %@",self.testString); }]; } - (void)dealloc{ NSLog(@""); } @end
我們首先來測試在實現block中沒有出現self及成員變數之類的,我們斷點在block類中,可以看一下block和testBlock的isa,我們會發現block和testBlock的isa都是一個NSGlobalBlock型別,然後在controller點選返回,斷點在dealloc,會發現會走斷點。
然後來測一下block中出現self或者成員變數時,先測
[self.model initWithBlock1:^{
NSLog(@"1111self====== %@",self.testString);
}];
這個方法裡面,未持有block,點選返回,dealloc的斷點依然會走。
再來測一下
[self.model initWithBlock2:^{
NSLog(@"2222self====== %@",self.testString);
}];
這個方法裡面,model持有了block,點選返回不會走dealloc,說明出現了迴圈引用。這個時候就可以用weakSelf和strongSelf來解決迴圈引用的問題了。
總結一下,當呼叫帶有block的方法時,如果該方法內部沒有持有該block,那麼這個方法在被呼叫時,就不需要考慮迴圈引用的問題。如果該方法內部持有了該block,那麼這個方法在被呼叫時,一定要注意block實現時是否會用到self或者self相關的,如果要用到,就需要用weakSelf和strongSelf來避免迴圈引用。
最後補充一點,block的isa有三種:
- NSGlobalBlock:儲存在程式的資料區域,在 block 內部沒有引用任何外部變數。
- NSStackBlock:儲存在棧上,在 block 內部引用外部變數。在 MRC 下,棧塊在當函式退出的時候,該空間會被回收,因此如果再呼叫該 block 會導致 crash,通過拷貝該棧塊,可以解決該問題。在 ARC 模式下,生成的 block 也是 棧塊,只是當賦值給 strong 物件時,系統會主動對其進行 copy。
- NSMallocBlock:儲存在堆上的 block。
我們都知道棧區的記憶體是系統自動管理的,所以出現NSStackBlock時我們可以不用考慮記憶體問題,但是出現NSMallocBlock時一定要注意了,很容易出現記憶體洩漏。