-(id)allocObject
{
id obj = [[NSObject alloc] init];
return obj;
}
id obj1 = [obj0 allocObject]; // 取得非自己生成的对象并持有该对象
-(id)object
{
id obj = [[NSObject alloc] init];
[obj autorelease];
return obj;
}
id obj1 = [obj0 object];
对于C语言中的局部变量而言,它只在其作用域内有效,超出作用域的局部变量不可被访问。autorelease类似地对待对象示例,对于超出作用域的对象,其release实例方法将被调用。使用方法如下:
1、生成并持有NSAutoReleasePool对象;
2、调用对象的autorelease方法,将其加入到AutoReleasePool中;
3、释放NSAutoReleasePool对象,对于所有调用过autorelease方法的实例对象,释放pool对象同时调用这些对象的release方法。
如下代码所示:
NSAutoReleasePool *pool = [NSAutoReleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain]; // 释放pool对象
autorelease的实现
-(id)autorelease
{
[NSAutoReleasePool addObject:self];
}
从代码中可以看出,autorelease方法的本质是调用NSAutoReleasePool的addObject类方法。
那么,如果调用NSAutoReleasePool对象的autorelease方法会发生什么结果?
结果就是发生异常。通常在使用Foundation框架时,无论调用哪一个对象的autorelease方法,其实现上都是调用NSObject类的autorelease实例方法,但是对于NSAutoReleasePool类而言,其autorelease方法已被该类重载,因此运行就会返回异常。
三、ARC
ARC即Automatic Reference Counting,自动引用计数,其本质还是引用计数式内存管理,但是正如名称中的“Automatic”所描述的那样,ARC只是自动地帮我们处理“引用计数”。
开启ARC的方法:
id obj = [[NSObject alloc] init];
id __strong obj = [[NSObject alloc] init];
__strong修饰符表示对对象的强引用,持有强引用的变量在超出其作用域时被自动废弃,而随着强引用的失效,变量所引用的对象也相应地被释放。
{
// 自己生成并持有对象
id __strong obj = [[NSObject alloc] init];
// 由于obj为强引用,故自己持有对象
}
// 由于变量obj超出其作用域,强引用失效,
// 自动释放自己持有的对象,对象的所有者已不存在,废弃该对象
由上可知,__strong修饰符通过变量的作用域可以正确地管理对象的所有者及其生命周期。
// obj0 持有对象A的强引用
id __strong obj0 = [[NSObject alloc] init]; // 对象A
// obj1 持有对象B的强引用
id __strong obj1 = [[NSObject alloc] init]; // 对象B
id __strong obj2 = nil; // obj2 不持有任何对象
obj0 = obj1;
// obj0持有对象B的强引用(由obj1赋值而来)
// obj0持有的对对象A的强引用失效,此时,对象A的所有者不存在,废弃对象A
// 此时,对象B的所有者为obj0和obj1
obj2 = obj0;
obj1 = nil;
obj0 = nil;
obj2 = nil;
// 此时,对象B的所有者不存在,废弃对象B
@interface Test : NSObject
{
id __strong obj_;
}
-(void)setObject:(id __strong)obj;
@end
@implementation Test
-(id)init
{
self = [super init];
return self;
}
-(void)setObject:(id __strong)obj
{
obj_ = obj;
}
@end
总结:通过使用__strong修饰符,可以达成“自己生成的对象,自己持有”和“非自己生成的对象,自己也能持有”,从而省去了retain代码。而通过废弃__strong变量(由于超过变量作用域或者成员变量所属的对象被废弃)或对变量重新赋值,可以做到“不再需要自己持有的对象时释放”。由于无需再写release代码,故也满足“非自己持有的对象无法释放”。这些都满足引用计数式的内存管理方式。
{
// test0持有对象A的强引用
id test0 = [[Test alloc] init];
// test1持有对象B的强引用
id test1 = [[Test alloc] init];
[test0 setObject:test1];
// 这句代码使得对象A的_obj成员变量持有对象B的强引用
// 此时,对象A的_obj变量和test1都持有对象B的强引用
[test1 setObject:test0];
// 对象B的_obj成员变量持有对象A的强引用
// 而此时,对象B的_obj变量和test0都持有对象A的强引用
}
// 由于test0变量超出作用域,强引用失效,自动释放对象A
// 由于test1变量超出作用域,强引用失效,自动释放对象B
// 此时,持有对象A的强引用的变量为对象B的_obj变量
// 持有对象B的强引用的变量为对象A的_obj变量
// 此时,由于对象A和对象B的_obj成员变量都相互持有对方的强引用
// 对象A和对象B在超出其生命周期后都无法被释放,发生内存泄露
id test = [[Test alloc] init];
[test setObject:test];
id __weak obj = [[NSObject alloc] init];
但是如果编译以上代码,编译器会发出警告,因为变量obj持有对象的弱引用,而弱引用不能持有对象的实例,这意味着生成的NSObject对象会立即被释放。
{
id __strong obj0 = [[NSObject alloc] init];
id __weak obj1 = obj0; // obj1变量持有对象的弱引用
}
由于__weak修饰符的变量不持有对象,因此,当变量超出其作用域时,对象会立即被释放。对于以下代码,先前使用__strong修饰符的情况下可能发生循环引用,但是将类的成员变量改成__weak修饰符的类成员变量的话,即可避免循环引用问题。
@interface Test : NSObject
{
id __weak obj_;
}
-(void)setObject : (id __strong) obj;
@end
__weak修饰符有另一优点,在持有某对象的弱引用时,若该对象被废弃,则此弱引用将自动失效且处于nil状态(空弱引用)
id __weak obj1 = nil;
{
id __strong obj0 = [[NSObject alloc] init];
obj1 = obj0;
NSLog(@"A: %@", obj1);
}
NSLog(@"B: %@", obj1);
执行结果:
A:
B: (null)
// ARC无效时
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];
// ARC有效时
@autoreleasepool{
id __autoreleasing obj = [[NSObject alloc] init];
}
@autoreleasepool{
// 取得非自己生成并持有的对象
id __strong obj = [NSMutableArray array];
// 变量obj为强引用,所以自己持有对象
// 而且,该对象由编译器判断其方法名后(非alloc/new/copy/mutableCopy开头)
// 自动注册到autoreleasepool
}
// 变量obj超出其作用域,强引用失效,自动释放自己持有的对象
// 同时,随着@autoreleasepool块的结束,注册到autoreleasepool中得所有对象被自动释放
// 因对象的所有者不存在,废弃对象
在访问__weak修饰的变量时,必定要访问注册到autoreleasepool的对象,这是因为:__weak修饰符只持有对象的弱引用,而在访问引用对象的过程中,该对象可能被废弃,而如果把要访问的对象注册到autoreleasepool中,在@autoreleasepool块结束之前都能确保该对象存在。
以下两种代码等价:
id __weak obj1 = obj0;
NSLog (@"class=%@", [obj1 class]);
id __weak obj1 = obj0;
id __autoreleasing tmp = obj1;
NSLog (@"class=%@", [tmp class]);