我理解的objective-C内存管理

    我开始学习iOS的时候,已经有ARC这个东西了,所以一开始就是在ARC的环境下学习,虽然对于内存管理有了解,但并没认真去处理这方面的问题。工作中的项目是以前开始开发,使用的是非ARC,而且项目已经初步成型,不好改成ARC,所以我又回头去研究内存管理。开始时很晕,不知那些地方改retain、那些地方该release,不过后来清楚了 许多,而且感觉挺喜欢这些东西,感觉对于程序的运行对了一个角度的认识,觉得很不错。以后新建项目,估计很少会不用ARC,只是觉得内存管理还是挺有意思的,而且是自己用心研究了的,就总结下。

    1、结构:

   从对象的角度来看整个程序,是一个嵌套的结构,首先是一个A,这个A的构建运行过程中又会用到许多其他的对象,而其他的对象再又会用到一些其他的对象,其实很像json的结构。为什么要先这样去看程序呢?这样可以把整个程序看成是一个对象包含了许多其他对象的结构,这样内存管理就可以分解为两部分:(1)需要释放的对象释放掉  (2)一个对象释放的时候,它所用到得对象都改被释放(除了某些还被其他对象使用的对象)

    举例说就是push到一个ViewController,然后再从那个ViewController返回,那么这个viewController会释放,这一般都不会出错,但是这个Viewcontroller的成员变量都释放了吗?这个ViewController方法中构建的临时对象都释放了吗?然后你可能在这个viewContrller里面使用了一个自定义的对象,假设为pruductCell,那么当你把改释放这个对象的地方都处理好了,就可以再去看看它的.m文件,是否它使用的对象也释放正确了。

    我觉得从这样一个结构去检查挺好的。

   2、一个对象释放的时候,它所用到得对象都改被释放(除了某些还被其他对象使用的对象):

    首先,对象的引用和释放要遵循:你retain的你要release,不是你retain的不要release(假设为准则1)

 一个对象(假设为A)使用到的其他对象,可以分为两类:一个是临时对象(假设为B),用完就可以释放;还一个是对象的属性、成员变量(假设为C),这个一般都会在对象A各个方法中被重复用到,这样就可以等到对象A被释放的时候再释放,也就是在A的dealloc方法里面释放。

    临时对象:

 这个很好处理,用完了就释放掉:

UIView * subView = [[UIView alloc]initWithFrame:CGRectMake(30, 100, 150, 260)];
    [self.view addSubview:subView]; //addsubView会retain子视图
    [subView release];
这里说的释放不是值释放内存,是指释放掉对这个临时对象的拥有权,就是执行release。对于拥有权,我的理解是对象A对某个对象(假设为B)进行了retain(包括alloc、copy等),这样其它引用了这个对象的对象(假设为C),只要遵循准则1,当对象C release了对象B之后,对象B又会回到之前的引用计数。这样只要对象A不release对象B,B的内存就永远不会释放。这就像对象A是拥有了对象B。

   而对临时对象release,这样当引用它的对象也release它之后,它就没有被任何对象拥有,内存就会被释放,这样刚好。例如这里,当subView被从self.view上面移除后,基本就是不需要用这个subView的时候,这时它内存被释放,刚好。

  因为临时变量构建在方法里,出了这个方法就这个指针就没了,就没法释放对它的所有权了,所以必须在方法结束前release。

  属性、成员变量:

  为了在对象(A)存在的整个过程中,它的属性、成员变量都活着,必须不能release,否则可能会引用计数为0,从而导致下次用的时候它内存已经被释放了,从而程序崩溃:

_subView = [[UIView alloc]initWithFrame:CGRectMake(30, 100, 150, 260)];
    [self.view addSubview:_subView];
    [_subView release];
    
    [_subView removeFromSuperview]; 
    
    NSLog(@"%@",_subView); //程序崩溃
不是说成员变量就不释放,而是释放了,下次再用就没有了。而既然是成员变量、属性,那么就是会再多个方法里面用到的,甚至会被其他的类的对象使用,所以要保持它是“活的”。


 3、关于属性定义时的关键词retain、assign:

      使用属性定义变量时,自带set\get方法,如果在变量赋值的时候使用self.XXX = YYY来方式赋值,其实是调用了set方法,而与内存管理有关的问题是:如果属性定义时使用retain,set方法是:

-(void )setXXX:(UIView *)XXX{
    [XXX retain];
    [_XXX release];
    _XXX = XXX;
}
对新值会retain,对旧值会release,从而使得对传入的YYY获取拥有权,保证在self使用它期间,它的内存永不会被释放掉。这也遵循准则1。而使用assign却是简单赋值,没有retain\release的操作。

   如果属性是assign或者干脆没有使用self.XXX进行赋值,那么就不会retain赋值对象,那么:

UIView * view1 = [[UIView alloc]initWithFrame:CGRectMake(30, 100, 150, 260)];
    _XXX = view1;
    [view1 release];
这里不会崩溃,但是接下来_XXX就变成了僵尸指针了,它指向位置的内存被释放了,这也是assgin脆弱的地方,所以需要较长时间对某个对象的使用,最好使用retain,保证引用计数不会为0。

 4、关于autoRelease:

  首先类方法构建对象,返回值一般自带autoRelease,使用时注意下。

  为什么要使用autoRelease,我的理解是延缓release。因为我构建的,所以我要释放,但是如果我release了,这个对象内存就被释放了,那不就白构建了吗?可以构建,然后把它给需要使用的地方,然后再释放。比如带放回值得方法:

-(NSString *)createString{
    NSString * str = [[NSString alloc]init];
    return str;
}
如果我在方法里面不释放,然后外面使用的时候遵循准则1,那么最后这个str的引用计数会保持在1,不会被释放,这样很成问题。但是如果释放了,那return的就是空值,没意义了。autoRelease使用了,就可以return一个有意义的值,然后最后这个对象在自动释放池里能被释放,不用担心内存泄露。

 5、特殊的方法和对象:

   addSubview会对添加进来的视图retain,NSMutableArray的adObject也会retain添加的对象,block会对里面使用到的变量全部retain,很可能造成循环引用,NStimer在没有释放的时候,会retain构建是指定的target,想要释放掉target,就需要先停止NSTimer。

   字符串常量不需要我们管理内存。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章