码迷,mamicode.com
首页 > 其他好文 > 详细

对retain 和 assign的理解

时间:2015-06-17 20:00:48      阅读:162      评论:0      收藏:0      [点我收藏+]

标签:

从开始接触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方法,

 三、事例分析:

 

 

 

对retain 和 assign的理解

标签:

原文地址:http://www.cnblogs.com/liuziyu/p/4584108.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!