有时我们需要在一个对象生命周期结束的时候触发一个操作,希望当该对象dealloc的时候调用一个外部指定的block,但又不希望直接hook dealloc方法,这样侵入性太强了.怎么办呢?
打个广告:问题验证demo里有相关的验证,可以去看看
为什么不直接写个block的属性,在dealloc的时候调用呢?
下面介绍一个简单的实现方法:
通过一个category给外部暴露一个block注入的接口,内部将该block封装到一个寄生对象的dealloc中(Parasite),该寄生对象是以关联对象的形式与对象绑定。在对象dealloc的时候,触发对象关联对象的释放,从而释放寄生对象,寄生对象触发其dealloc同时触发block调用
- 原理:所有的寄生对象通过runtime的AssociatedObject机制与宿主共存亡,从而达到监控宿主生命周期的目的.
下面就是代码的实现:
NSObject+Guard.h
#import <Foundation/Foundation.h>
@interface NSObject (Guard)
/**
@brief 添加一个block,当该对象释放时被调用
**/
- (void)guard_addDeallocBlock:(void(^)(void))block;
@end
NSObject+Guard.m
#import "NSObject+Guard.h"
#import <objc/runtime.h>
/// 此为一 寄生类
@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
细节点:
寄生对象有一属性block,其实用于接收外部注入的block,好在寄生对象dealloc时,获取并调用
分类的实现中,关联对象为一可变数组,是为了让对象可以被注入多个释放回调(同时创建了用于存放释放回调的寄生对象)。统一由数组保存即可
思考
为什么要大费周章的加入runtime来注入block?对象直接创建一个block属性,在dealloc的时候调用,不也一样可以执行?
个人有两个不成熟的想法:
简单点:此方法可以为对象注入多个不同block,不再需要重新定义,麻烦
鸿观点:此方法是一个不侵入类的做法。不需要关联到具体类(即不需要在每个类的dealloc中都操作一遍,此为侵入),比较通用。
以上为个人浅见,欢迎各位大神参加讨论