iOS内存优化

2019-02-06  本文已影响0人  荒漠现甘泉

引起内存泄漏的原因

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

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等方法。

内存泄漏检测方法

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

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
上一篇下一篇

猜你喜欢

热点阅读