iOS 为对象添加一个释放时触发的Block

有时我们需要在一个对象生命周期结束的时候触发一个操作,希望当该对象dealloc的时候调用一个外部指定的block,但又不希望直接hook dealloc方法,这样侵入性太强了.怎么办呢?
打个广告:问题验证demo里有相关的验证,可以去看看

为什么不直接写个block的属性,在dealloc的时候调用呢?

下面介绍一个简单的实现方法:

通过一个category给外部暴露一个block注入的接口,内部将该block封装到一个寄生对象的dealloc中(Parasite),该寄生对象是以关联对象的形式与对象绑定。在对象dealloc的时候,触发对象关联对象的释放,从而释放寄生对象,寄生对象触发其dealloc同时触发block调用

  • 原理:所有的寄生对象通过runtime的AssociatedObject机制与宿主共存亡,从而达到监控宿主生命周期的目的.

下面就是代码的实现:

  • NSObject+Guard.h
#import 

@interface NSObject (Guard)

/**
 @brief 添加一个block,当该对象释放时被调用
 **/
- (void)guard_addDeallocBlock:(void(^)(void))block;

@end

  • NSObject+Guard.m
#import "NSObject+Guard.h"
#import 

/// 此为一 寄生类
@interface Parasite : NSObject
@property (nonatomic, copy) void(^deallocBlock)(void);
@end

@implementation Parasite

- (void)dealloc {
    if (self.deallocBlock) {
        self.deallocBlock();
    }
}

@end

@implementation NSObject (Guard)

- (void)guard_addDeallocBlock:(void(^)(void))block {
    ///self是用来区分NSObject的子类的。
    
    ///此处有个同步操作,防止多线程数据竞争的干扰
    @synchronized (self) {
        static NSString *kAssociatedKey = @"kAssociatedKey";
        /// 数组,用于存放多个寄生对象
        NSMutableArray *parasiteList = objc_getAssociatedObject(self, &kAssociatedKey);
        if (!parasiteList) {
            parasiteList = [NSMutableArray new];
            objc_setAssociatedObject(self, &kAssociatedKey, parasiteList, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
        /// 创建寄生类 存放block
        Parasite *parasite = [Parasite new];
        parasite.deallocBlock = block;
        [parasiteList addObject: parasite];
    }
}

@end

细节点:

  1. 寄生对象有一属性block,其实用于接收外部注入的block,好在寄生对象dealloc时,获取并调用

  2. 分类的实现中,关联对象为一可变数组,是为了让对象可以被注入多个释放回调(同时创建了用于存放释放回调的寄生对象)。统一由数组保存即可

思考

为什么要大费周章的加入runtime来注入block?对象直接创建一个block属性,在dealloc的时候调用,不也一样可以执行?

个人有两个不成熟的想法:

  1. 简单点:此方法可以为对象注入多个不同block,不再需要重新定义,麻烦

  2. 鸿观点:此方法是一个不侵入类的做法。不需要关联到具体类(即不需要在每个类的dealloc中都操作一遍,此为侵入),比较通用。

以上为个人浅见,欢迎各位大神参加讨论

你可能感兴趣的:(iOS 为对象添加一个释放时触发的Block)