前言網絡
在咱們開發的過程當中,block運用的很是普遍,爲了不寫過多的delegate或者是通知等,項目中會運用大量的block回調,雖然如今大部分的工程都是採用ARC,可是在ARC上面使用block更爲複雜,在ARC機制下block自動的被copy到堆上(若是是在stack,或者是全局區是不會形成循環引用的),更具體的怎麼個複雜狀況,我在這裏就不闡述了,你們能夠去參考網絡上的文章,已經寫得很全面了;你們能夠去參考學習他們的文章。函數
(一)本文主要描述在使用block回調過程當中一個比較容易產生循環引用的場景,即在block中引用了對象自己的成員變量或者說屬性;如下是我準備的一些方法,學習
#import "SuperObject.h" #import <UIKit/UIKit.h> @class SonObject; typedef void(^CustomerBlock)(); typedef void(^CustomerBlockWithPara)(SonObject *son); @interface SonObject : SuperObject @property (nonatomic,copy) CustomerBlock blockTest; @property (nonatomic,copy) NSString *age; - (void)demo:(CustomerBlock)block; - (void)demoPara:(CustomerBlockWithPara)paraBlock; - (void)excuteBlock; @end #import "SonObject.h" @interface SonObject () { CustomerBlock customerBlock; CustomerBlockWithPara customerBlockPara; } @end @implementation SonObject //+ (void)initialize //{ // NSLog(@"SonObject 執行initialize %@",[self class]); //} // //- (SonObject *)init //{ // NSLog(@"SonObject 執行init %@",[self class]); // return [super init]; //} - (void)demo:(CustomerBlock)block { customerBlock = block; // self.blockTest = block; // //NSLog(@"block address -- %@",self.blockTest); // NSLog(@"====demo=== %@",block); //self.blockTest(); } - (void)excuteBlock { self.age = @"20"; if (customerBlock) { customerBlock(); } if (customerBlockPara) { customerBlockPara(self); } } - (void)demoPara:(CustomerBlockWithPara)paraBlock { customerBlockPara = paraBlock; } - (void)dealloc { NSLog(@"dealloc"); } @end
(二)製造一個循環引用的例子,使用了SonObject中的age屬性。this
//例子一 SonObject *son = [[SonObject alloc] init]; [son demo:^{ if ([son.age isEqualToString:@"20"]) { NSLog(@"====%@",@"right"); } }]; [son excuteBlock]; //例子二 son.blockTest = ^{ son.age = @"30"; };
例子一:son引用了customerBlock(是SonObject中的全局成員變量),而後在customerBlock中又引用了SonObject的age屬性,所以形成了循環引用;(值得注意的是,個人編譯器中居然不提示Capturing 'demo' strongly in this block is likely to lead to a retain cycle),這讓我很費解;最後SonObject中的dealloc函數並無執行,即son對象並無被釋放掉。atom
例子二:比較明顯,son引用了blockTest屬性,blockTest屬性又引用了son的age屬性;(這回編譯器提示了警告Capturing 'demo' strongly in this block is likely to lead to a retain cycle),why?爲何例子一的沒有提示?我也在求解。code
(三)消除以上的循環引用,讓son這個對象得以釋放掉;對象
- (void)testBlock { //方法一,建立一個指向son的弱引用對象 SonObject *son = [[SonObject alloc] init]; __weak typeof(son) weakSelf = son; //NSLog(@"test address -- %@",_sonObject); [son demo:^{ if ([weakSelf.age isEqualToString:@"20"]) { NSLog(@"====%@",@"right"); } }]; [son excuteBlock]; // //例子一 // SonObject *son = [[SonObject alloc] init]; // // [son demo:^{ // // if ([son.age isEqualToString:@"20"]) { // // NSLog(@"====%@",@"right"); // } // }]; // // [son excuteBlock]; // // //例子二 // son.blockTest = ^{ // // son.age = @"30"; // }; //方法二,執行回調函數時,將自身(self)當作參數回傳到block,這樣就像是編譯器給咱們作了弱引用操做; son = [[SonObject alloc] init]; [son demoPara:^(SonObject *son) { if ([son.age isEqualToString:@"20"]) { NSLog(@"====%@",@"right"); } }]; [son excuteBlock]; }
執行testBlock後,你會發現dealloc被成功的執行(2次),即每一次建立的對象都被成功釋放掉了。內存
(四)總結開發
以上寫的內容是個人我的的理解,也許有些地方理解錯誤了,也但願你們多多的指出來,你們互相的學習,block形成的循環引用太常見,避免它,以防內存泄漏。編譯器