zoukankan      html  css  js  c++  java
  • 对retain 和 assign的理解

    从开始接触MRC的云山雾罩,到现在略知皮毛,一路辛酸不表,以此为记录总结!

    开篇,首先感谢两位前辈的阐述,给我了很大的帮助!

    http://blog.csdn.net/yanxunuser/article/details/6792850

    http://www.cocoachina.com/bbs/read.php?tid-12760.html

    此篇有多处粘贴复制自上述文章,多谢!

    一、引用计数是实例对象的内存回收唯一参考

    引用计数(retainCount)是OC管理对象引用的唯一依据,调用实例的release方法后,此属性减一,减到0时,对象的dealloc方法会自动被调用,进行内存回收操作,也就是说我们永不该手动调用对象的dealloc方法。

    下面是内存管理API的主要操作接口:

      1、alloc,allocWithZone,new(带初始化)

      为内存分配对象,retainCount为“1”,并返回实例

      2、release

      retainCount减“1”,减到0时调用此对象的dealloc方法

      3、retain

      retainCount 加“1”

      4、copy ,mutableCopy

      复制一个实例,retainCount数为“1”,返回此实例。所得到的对象是与其上下文无关的,独立的对象(干净的对象)。

      5、 autorelease

      在当前上下文的AutoreleasePool栈顶的autoreleasePool实例添加此对象,,当pool的retainCount为0的时候,会对里面的所有实例依次release,然后调用pool的dealloc方法,由于它的引用使OC由全手动管理上升到半自动化。

    二、OC内存管理准则

    A类:是+1操作:1、3、4

    B类:是-1操作:2、5(延时释放)

    内存管理准则如下:

    1,A和B类的调用次数保持一致。

    2,为了很好的保障准则1,以实例对象为单位,谁A了就谁B,没有第二者参与!(系统参与的系统自会管理,人为参与的形式只为上述五种)

    三、关于属性的assign、retain和copy

    例:

      @Property (nonatomic ,assign)NSString *var;

      @Synthsize var;

    当使用@Synthsize var后编译器会自动的生成getter 和 setter 方法,这是我们就可以使用“.”操作符来为var赋值。

    1、将属性声明为assign时,setter方法的实现是这样的:

    - (void)setter:(NSString *)str

      var = str;//仅仅是指针的传递,retainCount 没有发生变化

    var = [[NSString alloc]initWithString:@“aaa”];

    这时候,[var retainCount]的值会是1;

    如果此时我们这样写:

    self.var = [[NSString alloc]initWithString:@“aaa”];

    此时就会调用var的setter 方法,且这时候,[var retainCount]的值仍会是1;

    2、将属性声明为retain时,

      @Property (nonatomic ,retain)NSString *var;

      @Synthsize var;

    这时候setter方法的实现是这样的:

    - (void)setter:(NSString *)str

      [str retain];

       [var release];

       var = str;

    现在就一目了然,此时,需要先将要赋的值的引用计数+1,然后将var指向的对象-1(release),最后,才是将str赋值给var。

    这时如果使用赋值:     self.var = [[NSString alloc]initWithString:@“aaa”];

    则:[var retainCount]的值会是2;(alloc +1,setter +1)

    这样做的目的是为了防止内存被过度释放。

    因为: [NSString alloc]initWithString:@“aaa”] 这块内存区域可能不仅仅只有var 指向,可能存在另外一个变量var2也指向此区域,

    若此时的var属性为assign,则即使调用赋值方法 self.var = [[NSString alloc]initWithString:@“aaa”];这块内存区域的retainCount仍然是1.

    那么如果var2在某处之行了[var2 release],后果就是var2 和var 共同指向的那块内存区域的retainCount值变为0,所以这块内存区域就被释放了,那么var 也就成为了也指针了,这时再引用var就会出现错误,最常见的是 “sentMessage to a dealloc instance”

    但是需要注意的是:即使声明为retain属性,我们在赋值的时候也要采用如下形式:

    var = [[NSString alloc]initWithString:@“aaa”];

    而不是:

    self.var = [[NSString alloc]initWithString:@“aaa”];

    这样setter方法不会被调用,这块内存的retainCount值为1.

     3、关于“.”操作符

    "."操作符在OC中是方法的调用,比如:self.str和[self str]是一样的。

    如果我在.h文件中声明了一个方法:

    -(void)method;

    那么,我调用这个方法可以用两种方式:[self method]或self.method。

    这样说的话,我们为什么可以用诸如self.str这样的形式来表示一个变量呢,原因就在于OBJC中变量属性的机制。

    前面说过,定义一个变量str,加个assign或retain之类的属性后,再用@synthesize就可以生成相应的setter和getter方法了。

    这样,对于一个变量,就有了相应的赋值方法,于是,对于self.str这样的写法,实际上就是调用了str对应的setter或getter方法。

    换句话说,也是把setter或getter消息发送给和str。str这时就是一个方法名,而不仅仅是变量名了。

    所以如果没有对一个变量声明属性,也没有@synthesize来生成setter和getter方法,

    那么就不能用self.str这种形式,而只能用str=@"aaa",或者str1=str这样的形式来使用变量。

    于是也就有了一个的结论:用self.str这种形式,相当于调用setter或getter方法。

    也就会执行retain的操作。不用这种形式,就没用调用setter或getter方法,retain的操作也就无从谈起。

    4、关于什么时候用“self.”赋值

    先从官方文档中拷贝出一个例子:

    MyClass.h

    @interface MyClass : NSObject {

        MyObject *myObject;
    }
    @property (nonatomic, retain) MyObject *myObject;
    @end

    MyClass.m

    @synthesize myObject;
     
    -(id)init{
        if(self = [super init]){
            MyObject * aMyObject = [[MyObject alloc] init];
            self.myObject = aMyObject;
            [aMyObject release];
        }
        return self;
    }

    现在我们来看看内存管理的内容:

    先看间接赋值的:

    1.加self.

     MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;(实则是指向的那块内存区域的retainCount)

     self.myObject = aMyObject; //myObject retainCount = 2;
     [aMyObject release];//myObject retainCount = 1;//内存泄漏

    2. 不加self.

    MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;

    myObject = aMyObject; //myObject retainCount = 1;
    [aMyObject release];//对象己经被释放

    再看直接赋值的:

    3.加self.

    self.myObject = [[MyObject alloc] init]; //myObject retainCount = 2;//内存泄漏

    4. 不加self.

    myObject = [[MyObject alloc] init]; //myObject retainCount = 1;

    现在是不是有点晕, 我们先来把代码改一下, 官方的一种常见写法:

    MyClass.h

    @interface MyClass : NSObject {
        MyObject * _myObject;
    }
    @property (nonatomic, retain) MyObject *myObject;
    @end

    MyClass.m

    @synthesize myObject = _myObject;

    OK, 你现在再试下, 如果你用self._myObject = aMyObject; 或者 myObject = aMyObject; 你会得到一个错误, 为什么呢, 这里就是和Obj-c的存取方法有关了. 说白了很简单 , 大家都知道, @property (nonatomic, retain) MyObject *myObject; 是为一个属性设置存取方法, 只是平时我们用的方法名和属性名是一样的,现在你把它写成不同的名字, 就会很清楚了. _myObject是属性值本身, myObject则是存取方法名.

    以下一次性给出assign retain和copy的setter方法:

    // assign 
    -(void)setMyObject:(id)newValue{
        _myObject = newValue; 
    }
    // retain 
    -(void)setMyObject:(id)newValue{
        if (_myObject != newValue) { 
            [_myObject release]; 
            _myObject = [newValue retain]; 
        }  
    }
    // copy 
    -(void)setMyObject:(id)newValue{
        if (_myObject != newValue) { 
            [_myObject release]; 
            _myObject = [newValue copy]; 
        } 
    }

    当在“=”左边调用self.myObject时是调用setter方法,

    当在“=”右边调用self.myObject时是调用getter方法,

    下面来总结什么时候用"self."

    1. 当属性为assign时,用”self.“不会对内存产生实际的作用,其实际的效果只是指针的传递。
    2. 当属性为retain,且当希望此块内存不被释放的时候用"self.",使retainCount+1,防止内存被过渡释放,一般用于两个类之间正向传递参数。(在push或present之前的点语法赋值)
    3. 在类内部,一般直接调用实例变量赋值名,保证此块内存地址的retainCount为1(只在alloc时候+1),在dealloc时候只需release一次,就能将此块内粗释放,防止内存泄露。
    4. 当定义delegate时候,需要设置属性为assign,防止生成retainCircle。
    5. 在block内部,定义__block weakSelf指针,使用weakSelf.delegate 的形式,防止生成retainCircle。

     

  • 相关阅读:
    hdu5321 beautiful set(莫比乌斯反演)
    BZOJ 5104 Fib数列(二次剩余+BSGS)
    高次同余方程,二次同余方程学习笔记
    CF587F Duff is Mad(AC自动机+树状数组+分块)
    51nod 麦克打电话(AC自动机+树状数组)
    BZOJ 3881 [Coci2015]Divljak(AC自动机+树状数组)
    51nod 1526 分配笔名(Trie树+贪心)
    BZOJ 3790 神奇项链(回文自动机+线段树优化DP)
    CF666E Forensic Examination(后缀自动机+线段树合并)
    [HAOI2016]找相同字符(SAM+DP)
  • 原文地址:https://www.cnblogs.com/liuziyu/p/4584108.html
Copyright © 2011-2022 走看看