引用计数式内存管理的思考方式:
- 自己生成的对象,自己所持有。
- 非自己生成的对象,自己也可以持有。
- 不再需要自己持有的对象时释放。
- 非自己持有的对象无法释放。
对象操作 | Objective-c方法 |
生成并持有对象 | alloc/new/copy/mutableCopy方法 |
持有对象 | retain方法 |
释放对象 | release方法 |
废弃对象 | dealloc方法 |
Cocoa 框架中 Foundation 框架中的NSObject 类担负内存管理的职责。内存管理中的alloc/retain/release/dealloc方法分别指代NSObject类的alloc类方法,retain实例方法,
release实例方法和dealloc实例方法。
1、 自己生成的对象,自己所持有
使用下面名称开头的方法名意味着自己生成的对象只有自己持有。- alloc
- new
- copy
- mutaleCopy
//使用alloc类方法就能自己生成并持有对象,指向生成并持有对象的指针被赋给变量obj
id obj = [[NSObject alloc] init];
//使用new类方法也可以生成并持有对象,所以 alloc 和 new 二者是完全一致的
id obj = [NSObject new];
纠正:上面代码注释中说到alloc 和new 二者是完全一致的,这种说法是不正确的!其实应该是:[NSObject new] 和 [[NSObject alloc] init] 这两个语句的作用是一致的。
1. mutableCopy创建一个新的可变对象,并初始化为原对象的值,新对象的引用计数为 1;
2. copy 返回一个不可变对象。分两种情况:
(1)若原对象是不可变对象,那么返回原对象,并将其引用计数加 1;
(2)若原对象是可变对象,那么创建一个新的不可变对象,并初始化为原对象的值,新对象的引用计数为 1。
//创建一个不可变数组
NSArray *immutableArray = [[NSArray alloc] initWithObjects:@"one",@"two",@"three", nil];
//从不可变数组复制而来一个可变数组
NSMutableArray *mutableArray = [immutableArray mutableCopy];
[mutableArray addObject:@"four"];
NSLog(@"count = %ld",(unsigned long)[mutableArray count]); //输出: count = 4
NSMutableArray *array = [[NSMutableArray alloc] init];
array = [mutableArray copy];
//注意这里使用copy复制得到的是一个不可变的对象,尽管使用的是NSMutableArray类型。所以下面一条语句想向数组中添加元素,虽然编译成功,但是运行会出错。
[array addObject:@"five"]; //运行出错
进一步说,copy就是浅拷贝,mutableCopy就是深拷贝。
NSObject* obj = [[NSObject alloc] init];
NSLog(@"%li",(unsigned long)[obj retainCount]); // print: 1
[obj release];
NSLog(@"%li",(unsigned long)[obj retainCount]); // print: 1
这段代码两次都是输出 1 ;第一个输出 1 ,应该没有什么问题,因为alloc 使得引用计数 +1 ;然后 release ,那么引用计数值 -1 ,那么就是应该输出 0呀!这个是为什么呢?
Do not use this method. (required) | |
| This method is of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method. |
//取得对象的存在,但是自己并不持有对象
id obj = [NSMutableArray array];
//通过retain可以持有对象
[obj retain];
那么通过retain方法,非自己生成的对象也可以自己持有,就像跟用 alloc new copy mutableCopy 方法生成对象并持有一样。
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 100)];
[label release];
//已经释放的对象,不可以再访问
label.text = @"hello world";
//运行时会崩溃
4、
-(id)allocObject {
id obj = [[NSObject alloc]init];
return obj;
}
注意方法的命名规则,要以alloc new copy mutableCopy开头。
-(id)object {
id obj = [[NSObject alloc]init]; //自己持有对象
[obj autorelease]; //取得对象的存在,但是自己不持有对象
return obj;
}
注意方法的命名规则,不要以alloc new copy mutableCopy开头,因为不持有对象。
[obj autorelease]; 这个语句将obj对象注册到 autoreleasepool (自动释放缓冲池)中,在pool结束时自动调用release方法 。可以这样理解,让autoreleasepool 持有对象,让其负责对象的释放。
方法调用
id obj1 = [self allocObject]; //自己持有对象
id obj2 = [self object]; //自己不持有对象,只是取得对象存在
//当然也可以通过retain方法持有该对象
[obj2 retain];
//例子 1
id obj1 = [self allocObject]; //自己持有对象
[obj1 release]; //对象已经释放了
[obj1 release]; //释放已经非自己持有的对象时,即访问已经废弃的对象,程序崩溃
//例子 2
id obj2 = [self object]; //自己不持有对象,只是取得对象存在
[obj2 release]; //释放非自己持有的对象,程序崩溃
这里的 allocObject 方法和 object 方法同上。
- 在objective - c 的对象中存有引用计数。
- 调用 retain 或者 alloc new copy mutableCopy 方法,引用计数值 +1;
- 调用release 后,引用计数值 -1;
- 引用计数值为0时,调用 dealloc 方法废弃对象。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //生成并持有NSAutoreleasePool对象
id obj = [[NSObject alloc] init];
[obj autorelease]; // 调用已经分配对象的 autorelease
[pool drain]; // 废弃NSAutoreleasePool对象
最后一行的 [pool drain]; 等同于 [poolrelease];