有時我們需要在一個對象生命周期結束的時候觸發(fā)一個操作,希望當該對象dealloc的時候調(diào)用一個外部指定的block,但又不希望直接hook dealloc方法,這樣侵入性太強了.怎么辦呢训堆?
打個廣告:問題驗證demo里有相關的驗證蜻韭,可以去看看
為什么不直接寫個block的屬性,在dealloc的時候調(diào)用呢缔逛?
下面介紹一個簡單的實現(xiàn)方法:
通過一個category給外部暴露一個block注入的接口画侣,內(nèi)部將該block封裝到一個寄生對象的dealloc中(Parasite),該寄生對象是以關聯(lián)對象的形式與對象綁定冰悠。在對象dealloc的時候,觸發(fā)對象關聯(lián)對象的釋放配乱,從而釋放寄生對象溉卓,寄生對象觸發(fā)其dealloc同時觸發(fā)block調(diào)用
- 原理:所有的寄生對象通過runtime的AssociatedObject機制與宿主共存亡,從而達到監(jiān)控宿主生命周期的目的.
下面就是代碼的實現(xiàn):
NSObject+Guard.h
#import <Foundation/Foundation.h>
@interface NSObject (Guard)
/**
@brief 添加一個block,當該對象釋放時被調(diào)用
**/
- (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是用來區(qū)分NSObject的子類的皮迟。
///此處有個同步操作,防止多線程數(shù)據(jù)競爭的干擾
@synchronized (self) {
static NSString *kAssociatedKey = @"kAssociatedKey";
/// 數(shù)組桑寨,用于存放多個寄生對象
NSMutableArray *parasiteList = objc_getAssociatedObject(self, &kAssociatedKey);
if (!parasiteList) {
parasiteList = [NSMutableArray new];
objc_setAssociatedObject(self, &kAssociatedKey, parasiteList, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
/// 創(chuàng)建寄生類 存放block
Parasite *parasite = [Parasite new];
parasite.deallocBlock = block;
[parasiteList addObject: parasite];
}
}
@end
細節(jié)點:
寄生對象有一屬性block伏尼,其實用于接收外部注入的block,好在寄生對象dealloc時尉尾,獲取并調(diào)用
分類的實現(xiàn)中爆阶,關聯(lián)對象為一可變數(shù)組,是為了讓對象可以被注入多個釋放回調(diào)(同時創(chuàng)建了用于存放釋放回調(diào)的寄生對象)沙咏。統(tǒng)一由數(shù)組保存即可
思考
為什么要大費周章的加入runtime來注入block辨图?對象直接創(chuàng)建一個block屬性,在dealloc的時候調(diào)用芭碍,不也一樣可以執(zhí)行徒役?
個人有兩個不成熟的想法:
簡單點:此方法可以為對象注入多個不同block,不再需要重新定義窖壕,麻煩
鴻觀點:此方法是一個不侵入類的做法。不需要關聯(lián)到具體類(即不需要在每個類的dealloc中都操作一遍杉女,此為侵入)瞻讽,比較通用。
以上為個人淺見熏挎,歡迎各位大神參加討論