一、背景
上一篇我们知道如何通过xcode工具去探测出野指针问题,但是面临两个重大弊端
- 测试同学无法直接使用
- 人工debug测试,对于一些释放后再次使用的对象来说测试周期短还是无法获取
针对以上两个痛点,我们需要开发一款工具,嵌入我们的工程使随时随地可以进行野指针case扑获
二、检测原理
- 当对象调用dealloc时,仅解除对象的引用关系,然后将该对象变为僵尸对象,同时添加NSProxy协议用于接受消息进行消息转发打印异常信息
- 通过链表管理僵尸对象,对象包含指针p,原对象以及加入时间。
- 当僵尸对象在30秒以内被再次访问时,就会通过NSProxy转发原对象,调用方法,以及堆栈信息,抛给业务层,由业务层自行处理。
- 定时扫描僵尸对象,存留超过30秒的僵尸对象,将被清理,回收内存。
三、目标
- 拦截野指针防止crash
- 拦截野指针对战进行上报
四、方案评估
本篇方案:
这篇文章采用拦截dealloc构造zombie对象进行消息转发获取给已释放对象发消息的堆栈
原因
不管是使用hook c层的free方法还是上层oc对象的dealloc方法,实际底层都是特别消耗性能的,两者相比hook c层的free方法,覆盖面表广,消耗性能也多,所以综合我们的实际情况选择后者
五、具体实现
核心主要流程
外部使用工具
引入pod库
js
pod 'HuntingZombiesTool'
初始化配置
js
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
//当检测到野指针后的回调,由业务自行处理。可选择上报错误,也可以直接crash
ZombieConfig.share.throwInfo = ^(NSString * _Nonnull info) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:info userInfo:nil];
};
//司机侧示例代码
ZombieConfig.share.throwInfo = ^(NSString * _Nonnull info) {
NSLog(info);
//可以上报到自己分析平台
};
ZombieConfig.share.zombieActivityTime = 30;
ZombieConfig.share.classes = [self ownClassesInfo];//如果这里传空,默认是扫描所有类
[ZombieConfig.share startZombie];
return YES;
}
//获取本项目非系统库的类
- (NSArray <Class>*)ownClassesInfo {
NSMutableArray *resultArray = [NSMutableArray array];
unsigned int classCount;
const char **classes;
Dl_info info;
dladdr(&_mh_execute_header, &info);
classes = objc_copyClassNamesForImage(info.dli_fname, &classCount);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_apply(classCount, dispatch_get_global_queue(0, 0), ^(size_t iteration) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSString *className = [NSString stringWithCString:classes[iteration] encoding:NSUTF8StringEncoding];
Class class = NSClassFromString(className);
[resultArray addObject:class];
dispatch_semaphore_signal(semaphore);
});
return resultArray.mutableCopy;
}
测试类
js
# 该类设置mrc
- (void)tmpBtnClickd:(UIButton *)sender {
UIView* testObj = [[UIView alloc] init];
[testObj release];
[testObj setNeedsLayout];
}
效果
六、依赖信息
到这里就完毕了,感谢您的阅读,欢迎阅读我的其他文章!