初衷
之前买了Hopper以后有点手痒,于是又想到了拿微信开刀了(哎,谁叫微信东西多可以好好探索,无意冒犯请包涵)。看着看着,发现了DeallocHelper
这个名字奇怪的玩意,仔细看看功能不多但挺有趣。于是萌生了逆向然后把源码写出来的念头。
功能
DeallocHelper
这个东西其实就是一个哨兵对象,用来实现监视某个对象的释放情况,在目标对象释放的时候立即调用相关代码。这个东西在微信里面主要是通过MMDelegateCenter
来调用的,MMDelegateCenter
貌似是用于某种解耦合的目的。DeallocHelper
具体接口如下:
@interface DeallocHelper : XXUnknownSuperclass {
id _target;
id _callback;
}
@property(copy, nonatomic) id callback;
@property(assign, nonatomic) id target;
+(void)DettachObject:(id)object key:(const void*)key;
+(void)attachToObject:(id)object key:(const void*)key whenDeallocDoThis:(id)aThis;
@end
其中DettachObject:
方法命名有点怪异以外,其余的东西大概也能猜到了吧。不过,实现怎么写,还是要逆向看代码啊。
思路
之前在写PINKBindView
的时候,我也利用RAC实现了这种监视对象释放的功能。RAC可以通过rac_deallocDisposable
来addDisposable:
达到目的,具体内部实现是swizzle了该对象的dealloc方法。如果要自己动手实现的话,这种方法写起来还是比较麻烦的。
而微信的DeallocHelper
实质就是创建一个实例,然后objc_setAssociatedObject
来往目标对象保存实例(具体保存实例的原理可以参考Dive into Category);目标对象释放的时候,DeallocHelper
的实例也会一起释放,因此只要重写DeallocHelper
的dealloc
方法就可以到达监视目标对象释放的功能。
实现
我是看着伪码来写的,GitHub地址:https://github.com/ipinka/Wechat_DeallocHelper。其中看不习惯的DettachObject:
,还是改成了dettachObject:
。不过值得一提的是target
是unsafe_unretained
而不是weak
,因为DeallocHelper
的dealloc
方法中访问target
结果是nil的,大家可以试试。
对了,Demo写了两种不同的方式创建的NSString对象:
NSString *test1 = @"2";
NSString *test2 = [NSString stringWithFormat:@"%d", 2];
提示一下,test1
即使出了作用域也不会释放的,因为本质是常量。
总结
哨兵对象这类思路很好,可以将原本实现起来比较麻烦的功能,用简单的方式就实现了。类似的应用还有老谭的利用ARC解决遗忘unlock的毛病。这是我逆向学习研究的记录之一,目测会出一系列的博文吧(希望)。