逆向学习之一:微信的DeallocHelper

初衷

之前买了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_deallocDisposableaddDisposable:达到目的,具体内部实现是swizzle了该对象的dealloc方法。如果要自己动手实现的话,这种方法写起来还是比较麻烦的。
而微信的DeallocHelper实质就是创建一个实例,然后objc_setAssociatedObject来往目标对象保存实例(具体保存实例的原理可以参考Dive into Category);目标对象释放的时候,DeallocHelper的实例也会一起释放,因此只要重写DeallocHelperdealloc方法就可以到达监视目标对象释放的功能。

实现

我是看着伪码来写的,GitHub地址:https://github.com/ipinka/Wechat_DeallocHelper。其中看不习惯的DettachObject:,还是改成了dettachObject:。不过值得一提的是targetunsafe_unretained而不是weak,因为DeallocHelperdealloc方法中访问target结果是nil的,大家可以试试。

对了,Demo写了两种不同的方式创建的NSString对象:

NSString *test1 = @"2";

NSString *test2 = [NSString stringWithFormat:@"%d", 2];

提示一下,test1即使出了作用域也不会释放的,因为本质是常量。

总结

哨兵对象这类思路很好,可以将原本实现起来比较麻烦的功能,用简单的方式就实现了。类似的应用还有老谭的利用ARC解决遗忘unlock的毛病。这是我逆向学习研究的记录之一,目测会出一系列的博文吧(希望)。