iOS性能优化是一个比较复杂的问题,其中之一就是内存泄露检测,很多人会第一时间想到使用Instruments。由于学习成本比较高,专业详细的教程也比较少,在学习了基本介绍后就望而生畏了。今天浏览了微信读书团队的技术博客,发现了一个非常友好的内存泄露检测库MLeaksFinder
MLeaksFinder
简单介绍一下MLeaksFinder。
官方解释:
具体的方法是,为基类 NSObject 添加一个方法 -willDealloc 方法,该方法的作用是,先用一个弱指针指向 self,并在一小段时间(3秒)后,通过这个弱指针调用 -assertNotDealloc,而 -assertNotDealloc 主要作用是直接中断言。
核心代码:
- (BOOL)willDealloc {
__weak id weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf assertNotDealloc];
});
return YES;
}
- (void)assertNotDealloc {
NSAssert(NO, @“”);
}
这样,当我们认为某个对象应该要被释放了,在释放前调用这个方法,如果3秒后它被释放成功,weakSelf 就指向 nil,不会调用到 -assertNotDealloc 方法,也就不会中断言,如果它没被释放(泄露了),-assertNotDealloc 就会被调用中断言。这样,当一个 UIViewController 被 pop 或 dismiss 时(我们认为它应该要被释放了),我们遍历该 UIViewController 上的所有 view,依次调 -willDealloc,若3秒后没被释放,就会中断言。
例如在UIViewController的分类中,使用 Method Swizzling,hook掉了viewDidDisappear:
,viewWillAppear:
,dismissViewControllerAnimated:completion:
等方法,让他们都执行willDealloc方法,这样,在不入侵开发代码的情况下,为UIViewController添加了检查内存泄露的功能(AOP)。
安装
安装非常简单,直接在Podfile中添加pod 'MLeaksFinder'
,你不需要在任何文件中引入头文件,执行pod install
后,直接构建或者run一下就好了。
案例
在iOS中,比较常见的内存泄露场景就是循环引用。作为一个iOS工程师,应该时刻警惕循环引用带来的问题。然而在赶工或者稍有不慎的情况下,还是会出现一些有问题的代码。
对于Xcode来说,编译器会对编写代码中明显的循环引用进行提示,比如对于self.property持有的block中,使用self,Xcode就会显示警告。但是,对于以下代码,Xcode就不会警告:
LessonWithoutJobCell *cell = [tableView dequeueReusableCellWithIdentifier:reusedID1];
if (!cell) {
cell = [[LessonWithoutJobCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reusedID1];
}
cell.model = model;
[cell setEnterBlock:^{
if (self.classBlock) {
self.classBlock(model);
}
}];
return cell;
在tableView中的某一个cell,持有了一个block,在block中使用了self.classBlock,乍一看好像没有问题啊。
真的没有问题吗?
在这个控制器被pop后,MLeaksFinder立刻就弹出了弹窗
IMG_6481.PNG
点击Retain Cycle
IMG_6479.PNG
仔细想想,真的是出现循环引用。首先是self引用了tableView,tableView引用了cell,cell中的block引用了self,这样,在控制器被pop之后,这个view还存在着强引用,这样它的内存就得不到释放,这样就造成了循环引用,经历频繁的push和pop操作后,内存将会暴增。其实这是一个很低级的bug了,但是稍不注意,就被忽略了。
既然发现了问题,解决循环引用就非常简单了。在cell的block外面,把self定义为weak类型,打破循环引用:
······
__weak typeof(self) weakSelf = self;
······
//把block中的self换成weakSelf即可
[cell setEnterBlock:^{
if (weakSelf.classBlock) {
weakSelf.classBlock(model);
}
}];
至此,MLeaksFinder的简单使用就介绍完了,真的十分简单就找到了一些潜在的问题,对于工程几乎0入侵,强烈推荐使用。
网友评论