五、内存管理
第29条、理解引用计数
在 objc 中,对象具有计数器,保留一个引用计数值,当计数归零时,系统回收此对象,也就是说将此对象占据的内存标记为可重用;
当对象被回收时,若其内存还未被覆写,那么该对象仍然有效,为了避免不经意使用无效对象,一般 release 完之后需要设置指针值为 nil,避免出现“悬垂指针”;
NSNumber *number = [[NSNumber alloc] initWithInt:1337];
[array addObject:number];
[number release];
number = nil;
在属性存取方法中,setter 方法内的执行顺序为:保留新值、释放旧值、指向新值,可以避免 set 原值时出现的异常;
自动释放池:稍后递减计数,通常是下一次事件循环,而 release 是立即减少引用计数;
- (NSString*)stringValue {
NSString *str = [[NSString alloc] initWithFormat:@"I am %@", self];
return [str autorelease]
}
第30条、以 ARC 简化引用计数
ARC,自动引用计数,自动执行 retain,release,autorelease 操作,这些方法在 ARC 中不允许被手动调用,并且 ARC 在调用这些方法时,并不通过普通的 objc 消息派发机制,而是直接调用底层 C 语言版本,性能更好;
将内存管理语义和方法名关联是 objc 的惯例,若方法名以 alloc、new、copy、mutableCopy 开头,那么对象归调用者所有,负责释放方法所返回的对象;
+ (EOCPerson*)newPerson {
EOCPerson *person = [[EOCPerson alloc] init];
return person;
}
+ (EOCPerson*)somePerson {
EOCPerson *person = [[EOCPerson alloc] init];
// 在 ARC 中,实际运行的是 return [person autorelease];
return person;
}
- (void)doSomething {
EOCPerson *personOne = [EOCPerson newPerson];
EOCPerson *personTwo = [EOCPerson somePerson];
// 自动调用的是:
// [personOne release]
}
ARC 还能将互相抵消的 retain、release 、autorelease 相互约简;
_myPerson = [EOCPerson personWithName:@"Bob Smith"];
// 等价于:
// EOCPerson *tmp = [EOCPerson personWithName:@"Bob Smith"];
// _myPerson = [tmp retain];
// 方法 personWithName 的 autorelease 与 tmp 的 retain 可以相互抵消;
应用程序中,可以通过下列修饰符改变局部变量或实例变量的语义,
__strong: 默认语义
__unsafe_unretained: 不使用内存管理
__weak: 不保留对象,当对象被回收,值变为空
__autoreleasing: 当对象被按引用传递时,此值在方法返回时自动释放
使用修饰符打破保留环:
EOCNetworkFetcher * __weak weakFetcher = fetcher;
[fetcher startWithCompletion:^(BOOL success) {
NSLog(@"Finished fetching from %@", weakFetcher.url);
}];
ARC 负责对实例变量进行内存管理,dealloc 方法会被自动调用,同时该方法会自动调用父类的 dealloc,由于 ARC 只负责管理 objc 对象的内存,所以 CoreFoundation 对象与 C 语言声明的内存,需要开发者释放;
- (void)dealloc {
CFRelease(_coreFoundationObject)
free(_heapAllocateMemoryBlob)
}