标签:
从开始接触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方法,
三、事例分析:
标签:
原文地址:http://www.cnblogs.com/liuziyu/p/4584108.html