继上一篇之后,再来讲述iPhone内存管理的细节.
四:动态内存管理
第四节就内容实质来说跟第三节的object的拥有和丢弃政策是一样的,不过是从以代码为引导的动态形式讲述.所以你会在这一节看到许多代码例子帮助你理解.核心还是那些基本的规则.
1.基本知识
主要规则还是object所有权,文档在这里重复性太大,我就只写一些值得注意的地方. 我们会看到很多类提供以”+className…”这种形式的代码(下面有例子),这在文档中叫做”convenience constructors”,记住你用这种方法返回而来的object你是没有所有权的,所以没有责任去释放.
2.一些简单的例子
以下三个例子较为简单地介绍用alloc或convenience constructor或accessor method得到object的区别,看代码:
- (void)printHello {
NSString *string;
string = [[NSString alloc] initWithString:@"Hello"];
NSLog(string);
[string release];
}
你用alloc得到的string,有义务用release释放.
- (void)printHello {
NSString *string;
string = [NSString stringWithFormat:@"Hello"];
NSLog(string);
}
你用convenience constructor得到的string,放心地不用再管它了.
- (void)printWindowTitle {
NSString *string;
string = [myWindow title];
NSLog(string);
}
你用accessor method得到的string,跟convenience constructor一样,放心地不用再管它了.
3.使用Accessor Methods
官方文档的建议是:相对于频繁的使用retain和release,更好的做法是使用accessor methods.比如我有这么一个object,它的类声明是:
@interface Counter : NSObject {
NSNumber *count;
}
为了get和set这个count,你要定义两个accessor methods(下面的例子较为简单,详细的accessor methods在后面几节会讲述).get是:
- (NSNumber *)count {
return count;
}
set稍微复杂点:
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[count release];
// make the new assignment
count = newCount;
}
因为你要防止newCount被别人释放了,所以你需要有拥有权,同时把之前你占有所有权的那个count释放掉.
上面提到推荐使用accessor methods,不过在init方法和dealloc里则例外,比如在init里你需要这么写:
- init {
self = [super init];
if (self) {
count = [[NSNumber alloc] initWithInteger:0];
}
return self;
}
如果想灵活一点使其可以自己设定值,可以:
- initWithCount:(NSNumber *)startingCount {
self = [super init];
if (self) {
count = [startingCount copy];
}
return self;
}
而在dealloc里,则需要释放count:
- (void)dealloc {
[count release];
[super dealloc];
}
实现一个reset重置方法: 你可以有两种方法:
(1)使用convenience constructor,很省心地不用再考虑何时释放:
- (void)reset {
NSNumber *zero = [NSNumber numberWithInteger:0];
[self setCount:zero];
}
(2)使用传统的alloc,需要释放:
- (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[self setCount:zero];
[zero release];
}
常见错误 (1)不使用accessor methods,那么你经常会忘记retain或release而犯错,如:
- (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[count release];
count = zero;
}
(2)实例内存泄漏,如:
- (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[self setCount:zero];
}
忘记release后,这个zero你再也release不掉了.
(3)release没有所有权的实例,如:
- (void)reset {
NSNumber *zero = [NSNumber numberWithInteger:0];
[self setCount:zero];
[zero release];
}
4.经常引起困惑的情形
(1)使用集合类(如array, dictionary, 或 set)
当添加一个object给集合类的实例时,这个集合类的实例拥有所有权,当这个object从集合类的实例中移除时,或集合类的实例自身被release了,就会释放这个object的所有权.例如创建一个数的array,你可以:
NSMutableArray *array;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
NSNumber *convenienceNumber = [NSNumber numberWithInteger:i];
[array addObject:convenienceNumber];
}
没必要retain,array就有所有权.当然你也可以:
NSMutableArray *array;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger: i];
[array addObject:allocedNumber];
[allocedNumber release];
}
(2)从方法中返回objects
这个很简单了,跟第三节也有重复,直接给出四个情形,你先自己判断一下吧:
- (NSString *)fullName {
NSString *string = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
return string;
}
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName] autorelease];
return string;
}
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName] release];
return string;
}
- (NSString *)fullName {
NSString *string = [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName];
return string;
}
很明显吧,前两个是对的,后两个是扯淡的.若对这个还有疑问,直接回复提问吧.