美文网首页
iOS内存优化

iOS内存优化

作者: 荒漠现甘泉 | 来源:发表于2019-02-06 10:01 被阅读0次

    引起内存泄漏的原因

    引起内存泄漏的原因主要有三类,如下

    • 循环引用
    • 强引用
    • 非OC对象

    1、循环引用。最简单的循环引用如下:

    ClassA* a = [ClassA new];
    ClassB* b = [ClassB new];
    a.b = b;
    b.a = a;
    
    

    对象a引用对象b,对象b引用对象a,对象a在等着b释放,对象b在等着对象a释放,结果谁也释放不了谁,因而造成了内存泄漏。
       在平时写代码过程中,最常见的循环引用还是使用代理及block的时候。
           (1)使用代理指定代理的时候没有用weak或者assign(当然推荐用weak,具体原因不在本篇论述范围),而是使用了strong,导致代理及被代理对象相互引用,无法释放。
           (2)使用block的时候引用自身的时候。当然不是所有使用block在block内引用自身的时候的都会引起内存泄漏,只有对象本身直接或间接引用了block对象,并且在block内引用了对象自身的时候才会造成内存泄漏,这时就需要使用weakSelf了,如下:

    @property (nonatomic, copy) dispatch_block_t block;
    @property (nonatomic, strong) NSString* str;
    
    
    
        // self -> block -> self
        //
        __weak typeof (self) weakSelf = self;
        self.block = ^{
            NSLog(@"%@", weakSelf.str);
        };
        
        self.block();
    
    

    不过如果上述代码在block内部进行了延迟调用self,就不能单纯使用weakSelf,否则调用会失效,必须在block内部强引用一次,如下:

        // self -> block -> self
        //
        __weak typeof (self) weakSelf = self;
        self.block = ^{
            
            __strong typeof (self) strongSelf = weakSelf;
          
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"%@", strongSelf.str);
            });
        };
        
        self.block();
    
    

    2、强引用。当一个常驻内存的对象(静态对象)强引用了某一个变量时,该变量也无法释放,例如单例引用的对象,再者如下NSTimer的引用:

    @interface TZViewController ()
    @property (nonatomic, strong) NSTimer* timer;
    @end
    
    
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
    
    

    在上述TZViewController中,当controller退出的时候,controller和NSTimer对象也不会释放。NSTimer对象创建激发的时候是添加到Runloop中,持有关系如下:

    RunLoop -> timer -> target -> self
    
    

    在上述关系中,RunLoop持有了timer,timer通过target持有了self,所以timer和controller都无法释放。具体解决办法可以从上述持有关系中找,只要打破了上述持有链条中的一个,controller对象就能释放。比如可以在contrller对象调用dealloc方法之前停止并且释放timer,或者timer持有self的时候,弱引用self都可以做到。
           3、非OC对象。主要是一些CG类对象、CF类对象,c语言对象,不会自动释放,需要调用相应的CGRelease,CFRelease,free等方法。

    内存泄漏检测方法

    内存泄漏的检测方法主要有如下三类:

    • 静态检测方法(手动、自动)
    • 动态监测方法(instruments 第三方工具检测)
    • dealloc
    • 野指针的检测

    1、静态检测方法
           在Xcode --> Product --> Analyze 即可进行静态分析。如下图所示:

    静态检测结果示意图
    可以看到内存泄漏等代码错误信息。这是手动的静态检测方法,此外也可以<font color = red>通过Build Settings --> Analyze During 'Build' 修改为Yes(默认为No)</font>,这样每次在编译运行的时候,项目就会自动进行静态分析了。
    2、动态检测方法
           (1)instruments
    使用操作Xcode --> Product --> Profile可以启动instruments,选择Leaks,可以进行内存泄漏的检测。如下图是运行Leaks的截图,点击红色的叉在底部可以看到相关的内存泄漏的信息。
    instruments1.png
    下面的这张图还能具象的看到内存泄漏,循环引用的抽象图。
    instruments2.png
    如下是调用树的图。
    instruments3.png
    (2)第三方检测工具MLeaksFinder。通过MLeaksFinder在调试的时候可以检测UIViewController的内存泄漏.如下图所示:
    MLeaksFinder1.png
    点击上图的Retain Cycle会弹出调用树信息,同时也会打印调用树的日志。如下:
    MLeaksFinder2.png
    3、dealloc。通过在dealloc方法打断点查看有没有内存泄漏。
    4、野指针的检测。通过Edit Scheme --> Run --> Diagnostics --> 勾选Zombie Objects可以进行僵尸对象,即野指针的检测。下图可以看到检测的信息.
    野指针.png

    相关文章

      网友评论

          本文标题:iOS内存优化

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