博客传送门
最近做直播间优化时遇到个奇葩需求,需要在评论输入视图释放时做一些操作,当时没想太多直接在CommentView外部公开一个block,然后在CommentView的dealloc
方法里调用该block,简单粗暴...事后想了想能不能封装一个通用的方法给对象添加一个释放时调用的block呢?第一时间想到用Method Swizzling来hook对象的dealloc
方法,马上领悟到这样未免侵入性太强,日后也许会导致难以排查的bug,仔细思考后想到了一个巧妙的方法,通过Category给NSObject
公开一个添加block的方法,利用runtime的AssociatedObject给对象搭配一个‘‘小伙伴’’,在对象释放时,这个‘‘小伙伴’’负责调用block,这样既做到了与对象的共存亡又简单干净,上代码~
#import <Foundation/Foundation.h>
typedef void(^DeallocBlock)(void);
@interface NSObject (Associate)
/**
给对象添加释放时调用的block
*/
- (void)addDeallocBlock:(DeallocBlock)deallocBlock;
@end
#import "NSObject+Associate.h"
#import <objc/runtime.h>
@interface Partner : NSObject
@property (nonatomic, copy) DeallocBlock deallocBlock;
@end
@implementation Partner
- (void)dealloc {
if (_deallocBlock) {
_deallocBlock();
}
}
@end
const void *kPartnerArrayKey = &kPartnerArrayKey;
@implementation NSObject (Associate)
- (void)addDeallocBlock:(DeallocBlock)deallocBlock {
@synchronized (self) {
NSMutableArray *partnerArray = objc_getAssociatedObject(self, kPartnerArrayKey);
if (!partnerArray) {
partnerArray = [NSMutableArray array];
objc_setAssociatedObject(self, kPartnerArrayKey, partnerArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Partner *partner = [[Partner alloc] init];
partner.deallocBlock = deallocBlock;
[partnerArray addObject:partner];
}
}
@end
这里有几点注意事项:
1.block触发时的线程与对象释放时的线程一致,注意后续操作的线程安全。
2.如果你想在block里引用对象,那么无论强弱引用都是不可行的,强引用会造成循环引用,弱引用则获取不到对象,因为已经被至为nil,可以使用__unsafe_unretained
修饰:
__unsafe_unretained typeof(self) unsafeSelf = self;
现在看起来比最开始的直接在CommentView的dealloc里做操作的想法成熟多了,但之后我再查阅相关资料时还是发现自己too young too native了...其实早就有大佬写了类似的框架,放个链接供大家膜CYLDeallocBlockExecutor,代码不是很复杂,但是考虑的点和实现思路很赞,也许这就是差距吧...其中涉及到了NSHashTable
,不了解的可以看南大的iOS知识小集 第1期(2015.05.10)-NSHashTable部分。