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
  1. 不要主动调用另外一个对象的dealloc方法
  2. 在dealloc方法后必须主动调用super(MRC环境下)
  3. 不要将系统资源的管理与对象声明周期相关联
  4. 当应用被杀死时,对象不会收到dealloc方法,因为应用进程内存都会在退出时进行清理

Core Foundation的内存管理规则比较

Core Foundation对象的内存管理规则和Cocoa相似,但是其命名约定是不同的,特别是Core Foundation的Create Rule不适用于返回Objective-C对象的方法。
例如:

MyClass *myInstance = [MyClass createInstance];