Block循环引用

一曲冷凌霜 提交于 2019-12-10 05:51:03

前言

在我们开发的过程中,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属性。

//例子一
    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对象并没有被释放掉。

例子二:比较明显,son引用了blockTest属性,blockTest属性又引用了son的age属性;(这回编译器提示了警告Capturing 'demo' strongly in this block is likely to lead to a retain cycle),why?为什么例子一的没有提示?我也在求解。

 

(三)消除以上的循环引用,让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造成的循环引用太常见,避免它,以防内存泄漏。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!