美文网首页程序员
iOS性能优化之内存泄露检测

iOS性能优化之内存泄露检测

作者: 动物园园长熊熊酱 | 来源:发表于2018-12-04 16:51 被阅读160次

    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入侵,强烈推荐使用。

    相关文章

      网友评论

        本文标题:iOS性能优化之内存泄露检测

        本文链接:https://www.haomeiwen.com/subject/qszmcqtx.html