Block循環引用

前言網絡

在咱們開發的過程當中,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形成的循環引用太常見,避免它,以防內存泄漏。編譯器

相關文章
相關標籤/搜索