项目的代码不少,前两天老大忽然跟我说项目中某一个ViewController的dealloc()方法没有被调用,存在内存泄漏问题,须要排查缘由,解决内存泄漏问题。因为刚加入项目组不久,对出问题的模块的代码还不太熟悉,因此刚拿到问题时以为很棘手,再加上做为一个iOS菜鸟,对内存泄漏的排查方法和缘由确实基本上不了解。因此,也借着这样的机会,我研究了一下关于iOS开发中内存泄漏的排查方法和缘由分析。xcode
首先,补充两个基本概念的解释:ide
咱们知道,iOS开发中对内存管理的要求很是严格,一旦存在内存泄漏,后果是很是严重的,会致使程序很是容易崩溃。尽管目前iOS开发基本上都是采用的ARC方式进行内存管理,可是一不当心就会存在内存泄漏的问题。工具
首先,咱们须要定位内存泄漏的问题,目前比较经常使用的内存泄漏的排查方法有两种,都在xcode中能够直接使用:静态分析方法(Analyze)和动态分析方法(Instrument的leak)。ui
经过xcode打开项目,而后点击product-->Analyze,以下图左侧的图所示,这样就开始对项目进行静态内存泄漏分析,分析结果以下图右侧的图所示。根据分析结果进行休整以后在进行分析就行了。spa
静态分析方法能发现大部分的问题,可是只能是静态分析结果,有一些并不许确,还有一些动态分配内存的情形并无进行分析。因此仅仅使用静态内存泄漏分析获得的结果并非很是可靠,若是须要,咱们须要将对项目进行更为完善的内存泄漏分析和排查。那就须要用到咱们下面要介绍的动态内存泄漏分析方法Instruments中的Leaks方法进行排查。3d
分析内存泄露不能把全部的内存泄露查出来,有的内存泄露是在运行时,用户操做时才产生的。那就须要用到Instruments了。具体操做是经过xcode打开项目,而后点击product-->profile,以下图左侧图所示。代理
按上面操做,build成功后跳出Instruments工具,如上图右侧图所示。选择Leaks选项,点击右下角的【choose】按钮,这时候项目程序也在模拟器或手机上运行起来了,在手机或模拟器上对程序进行操做,工具显示效果以下:code
点击左上角的红色圆点,这时项目开始启动了,因为leaks是动态监测,因此手动进行一系列操做,可检查项目中是否存在内存泄漏问题。如图所示,橙色矩形框中所示绿色为正常,若是出现如右侧红色矩形框中显示红色,则表示出现内存泄漏。对象
选中Leaks Checks,在Details所在栏中选择CallTree,而且在右下角勾选Invert Call Tree 和Hide System Libraries,会发现显示若干行代码,双击便可跳转到出现内存泄漏的地方,修改便可。blog
在目前主要以ARC进行内存管理的开发模式,致使内存泄漏的根本缘由是代码中存在循环引用,从而致使一些内存没法释放,这就会致使dealloc()方法没法被调用。主要缘由大概有一下几种类型。
若是你的ViewController中有NSTimer,那么你就要注意了,由于当你调用
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];
时的 target:self 就增长了ViewController的return count,若是你不将这个timer invalidate,将别想调用dealloc。
一个比较隐秘的因素,你去找找与这个类有关的代理,有没有强引用属性?若是你这个VC须要外部传某个Delegate进来,来经过Delegate+protocol的方式传参数给其余对象,那么这个delegate必定不要强引用,尽可能assign或者weak,不然你的VC会持续持有这个delegate,直到它自身被释放。
这个可能就是常常容易犯的一个问题了,Block体内使用实例变量也会形成循环引用,使得拥有这个实例的对象不能释放。由于该block原本就是当前viewcontroller的一部分,如今盖子部门又强引用self,致使循环引用没法释放。 例如你这个类叫OneViewController,有个属性是NSString *name; 若是你在block体中使用了self.name,或者_name,那样子的话这个类就无法释放。 要解决这个问题其实很简单,就是在block以前申明当前的self引用为弱引用便可。
//MRC下代码以下 __block Viewcontroller *weakSelf = self; //ARC下代码以下 __weak Viewcontroller *weakSelf = self;
这个问题也是个人项目中内存泄漏的问题所在。咱们有时候须要在子视图或者某个cell中点击跳转等操做,须要在子视图或cell中持有当前的ViewController对象,这样跳转以后的back键才能直接返回该页面,同时也不销毁当前ViewController。此时,你就要注意在子视图或者cell中对当前页面的持有对象不能是强引用,尽可能assign或者weak,不然会形成循环引用,内存没法释放。