Demo:https://github.com/panghaijiao/HJNSObjectReleaseDemo
iOS内存泄露一直是程序猿需要注意的问题,目前来说,iOS官方提供内存泄露检测的工具主要是xcode instrument,网上相关文档比较多,在次就不再介绍。但是instrument检测有几个问题:
- 由于其功能过于强大,导致其检测时间比较长, 程序猿不可能经常使用;
- 无法检测一些特殊对象的泄露,如被单例引用过的对象,或者Block循环引用的对象;
- 内存泄露对象提示位置不准,其提示泄露的位置很多时候并不是对象实际泄露的位置,有可能是其引用类泄露所致,程序猿因此无法精准定位实际泄露对象,当然,对于高级程序猿,可能会详细追溯泄露的根源,不足在于代价较大。
本期主要介绍HJNSObjectRelease内存检测工具。
iOS对象生命期可以抽象为创建和释放两个过程,只有创建没有释放就表明该对象泄露。基于此原则,可以监控任何对象的创建和释放过程,那么问题来了,如何检测对象的创建和释放?
1 2 3 4 5 6 7 8 9 |
- (instancetype)HJInit { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(HJReceiveReleaseNotice) name:HJNSObjectNoticeName object:nil]; return [self HJInit]; } Method ori_Method = class_getInstanceMethod([NSObject class], @selector(init)); Method HJ_Method = class_getInstanceMethod([NSObject class], @selector(HJInit)); method_exchangeImplementations(ori_Method, HJ_Method); |
基于oc动态语言的特性,在对象init之前为对象创建一个通知监听,同时在对象dealloc时将其从监控中移除
1 2 3 4 5 6 7 8 9 10 |
- (void)HJDealloc { [self HJRemoveObserver]; [self HJDealloc]; } - (void)HJRemoveObserver { [[NSNotificationCenter defaultCenter] removeObserver:self name:HJNSObjectNoticeName object:nil]; } |
此时,通过发送通知,那些泄露的对象由于没有调用dealloc函数(没有将自己从监控中移除)就会将自己的class打印在控制台,所有泄露对象一目了然。
1 2 3 4 5 6 7 8 9 |
+ (void)sendReleaseNotice { @autoreleasepool { HJNSObjectArray = [NSMutableArray array]; [[NSNotificationCenter defaultCenter] postNotificationName:HJNSObjectNoticeName object:nil]; NSLog(@"HJNSObjectLeak: %@", HJNSObjectArray); [HJNSObjectArray removeAllObjects]; } } |
发送通知可以设定在任意时刻,比较好的方式是在AppDelegate对象中
1 2 3 4 5 6 7 8 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [HJNSObjectRelease createReleaseObserver]; return YES; } - (void)applicationDidEnterBackground:(UIApplication *)application { [HJNSObjectRelease sendReleaseNotice]; } |
使用方法:
以测试view为例
1 2 3 4 5 6 7 8 9 10 |
- (IBAction)onViewTestClick:(id)sender { //首先通过进入后台的方式或手动调用方式清空 //[HJNSObjectRelease sendReleaseNotice]; [HJReleaseView new]; //然后以相同的方式(进入后台或手动调用)发送打印通知 //[HJNSObjectRelease sendReleaseNotice]; } |
此时控制台就会列出泄露的所有对象,是不是很简单!
1 2 3 4 |
2014-10-21 23:53:06.898 HJNSObjectReleaseDemo[8519:154606] HJNSObjectLeak: ( HJReleaseView, "UIImageView->HJReleaseView" ) |
PS:iOS9启动卡死问题已修复 2016/3/31
谢谢来访
看了你的文章,有两点疑问?
1. 你method swizzling了init方法。那么OC中默认的init方法是否没有被调用到了?
2.看你文章中的代码,发现只有init方法的swizzling,那么dealloc方法呢?应该跟init方法的swizzling差不多。但是想问下。同样的框架中默认的dealloc方法应该是不会被调用到。
如何保证编译器为dealloc添加的代码都被执行了呢?
希望能够交流下想法。谢谢!:)
1、本工具的核心就是swizzling对象的init和dealloc方法,在自定义的方法里面只是增加了监听,最后还是会回调系统的init的方法的,所以不会影响对象的init和dealloc方法!
2、dealloc也被swizzling了,具体可以查看代码,swizzling针对所有NSObject对象都有效,属于动态运行的,正常情况swizzling函数都会执行,除非被其它 swizzling给覆盖!
楼主,怎样区分leak的对象和正处生命周期中的正常对象,代码中貌似所有没有dealloc的对象都能打印出来。
是这个的,本工具的主要目的是快速检测所有没有被释放的对象,包括单例对象等,比较适合分模块检测,要区分是否leak对象还是推荐instruments工具,具体区别及使用场景可以查看文章详细解决!
Pingback: [转] iOS — “自释放”在iOS开发中的应用 - 移动开发 - 阿里欧歌
在iOS9.2下Demo会有闪退的现象,有劳博主看下~
谢谢提醒,估计新版本有变动,我先看下
Pingback: [转]iOS—"自释放"在iOS开发中的应用 | 安卓文档网
重写了init的单例无法使用?
建议在模拟器8.X版本运行,9.X版本系统改动较大,有点问题~
9.x问题已修复
[NSObject new];
没有 检测到
[NSObject new];
没有检测到
NSObject可以检测到,只是由于没有任何价值,被过滤掉了
请问,dealloc方法的调用不是根据计数器来判断的吗?
你是怎么知道什么时候该调用dealloc
还有就是没有调用dealloc的对象不一定是内存泄漏吧? a 引用 b , c 引用 b , a dealloc了 b并没有dealloc,这个时候b的监听没有执行, 那 b 是内存泄漏吗?
搞不懂这个逻辑