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形成的循环引用太常见,避免它,以防内存泄漏。编译器

相关文章
相关标签/搜索