Cocoa基本内存管理规则
基本内存管理规则
内存管理是基于对象所有权进行管理的,任一对象都可能有1个或者多个持有者,只要至少有一个持有者,该对象就会一直存在,否则加入没有了持有者,运行时系统就会将其自动销毁,为确保你可以清楚管理自己持有对象,Cocoa
设置了基本的内存分配原则:
自己生成的对象,自己持有
当你使用`alloc`、`new`、`copy`、`mutableCopy`开头的方法创建一个对象时
alloc或者new就自己生成对象并持有
copy方法利用基于`NSCopying`协议约定,由各类实现`copyWithZone:`方法生成并持有对象副本;mutableCopy方法与其类似,只是其利用基于`NSMutableCopying`协议约定,由各类实现`mutableCopyWithZone:`方法生成并持有对象的副本。而两者区别在于:copy生成的时可变对象,而mutableCopy生成了可变更对象。虽然生成的是副本,但是仍然是生成并持有对象
非自己生成的对象,自己也可以使用retain
持有
用除了上面的方法外生成的对象,所以并非自己生成的对象持有,可以使用retain进行持有
为了保证接收到的对象在接收到的方法中保持有效,并且该方法也可以安全的将对象返回给其调用者。通常需要在以下两种情况下`retain`:
1. 在访问器方法和初始化方法实现中,对想要作为属性存储的对象进行持有
2. 防止对象因为其他操作的副作用而失效(例如从数组移除或父对象被删除)
当你不再需要该对象,释放
自己持有的对象,当不再需要时需要,通过`release`或者`autorelease`方法放弃对象的持有权
非自己持有的对象无法释放
例如:
id obj = [NSMutableArray array];
//释放了非自己持有的对象
[obj release];
内存管理方法的实现
苹果管理对象内存引用计数的方法大概是通过散列表(引用计数表)的方式,这张表的键位内存块地址的散列值(hash值),而值即为该对象内存的引用计数
而至于retainCount/retain/release
方法实现是通过CoreFoundation中的__CFDoExternRefOperation()
函数,通过不同的操作将表中对象内存地址对应的引用计数进行操作
示例
0.可以通过retainCount获取对象引用计数
//可以通过retainCount获取对象引用计数
id obj = [[NSObject alloc] init];
NSLog(@"%ld", [obj retainCount]);
1.不在需要时使用release释放
//自己生成持有的对象 自己释放
{
id obj = [[NSObject alloc] init];
[obj release];
}
//非自己生成持有的对象,自己释放
{
id array = [NSMutableArray array];
[array retain];
[array release];
}
2. 使用autorelease来延时释放
当需要用某个方法生成对象,并返回给调用方:
- (id)allocObject {
id obj = [[NSObject alloc] init];
return obj;
}
因为调用方法名满足以alloc
开头条件,因此返回生成并持有的对象,就可以让调用方也持有该对象
那么类似[NSMutableArray Array]生成对象,但是不持有对象是如何实现的:
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
self.firstName, self.lastName] autorelease];
return string;
}
根据内存管理规则,你必须释放alloc
对象,但是如果直接release
,string就会在返回前被deallocated(方法就会返回一个无效对象),因此我们可以使用autorelease
,表示想要释放对象,但是允许方法调用者在释放之前使用方法的返回值
autorelease
会使对象继续存在 但是自己并不持有对象
这种方法的命名开头不能为alloc/mew/copy/mutablerCopy
还可以这么写:
- (NSString *)fullName {
NSString *string = [NSString stringWithFormat:@"%@ %@",
self.firstName, self.lastName];
return string;
}
此时string
对象自己并不持有,因此也不负责释放,可以安全的返回给方法的调用者
3.你不持有通过引用返回的对象
在cocoa
方法中一些对象通过引用返回(即这些方法有参数为class **
或者id *
),例如常用的NSError
对象
NSString *fileName = <#Get a file name#>;
NSError *error;
NSString *string = [[NSString alloc] initWithContentsOfFile:fileName
encoding:NSUTF8StringEncoding error:&error];
if (string == nil) {
// Deal with error...
}
// ...
[string release];
当你调用这些方法时,你并不创建NSError
对象,你并不持有该对象,因此也无需release
使用dealloc放弃对象的所有权
NSObject
类中定义了dealloc
方法,当对象没有了持有者或者需要被回收时,就会调用该方法.其作用是释放对象内存,并且处置其拥有的资源,包括释放其持有的所有对象
@interface Person : NSObject
@property (retain) NSString *firstName;
@property (retain) NSString *lastName;
@property (assign, readonly) NSString *fullName;
@end
@implementation Person
// ...
- (void)dealloc
[_firstName release];
[_lastName release];
[super dealloc];
}
@end
- 不要主动调用另外一个对象的dealloc方法
- 在dealloc方法后必须主动调用super(MRC环境下)
- 不要将系统资源的管理与对象声明周期相关联
- 当应用被杀死时,对象不会收到
dealloc
方法,因为应用进程内存都会在退出时进行清理
Core Foundation的内存管理规则比较
Core Foundation
对象的内存管理规则和Cocoa相似,但是其命名约定是不同的,特别是Core Foundation的Create Rule不适用于返回Objective-C对象的方法。
例如:
MyClass *myInstance = [MyClass createInstance];