版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.10.07 |
前言
Core Foundation
框架(CoreFoundation.framework)
是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。接下来我们就详细的解析这个框架。感兴趣的可以看我上面写的几篇。
1. CoreFoundation框架详细解析(一) —— 基本概览
2. CoreFoundation框架详细解析(二) —— 设计概念
3. CoreFoundation框架详细解析(三) —— 内存管理(一)
4. CoreFoundation框架详细解析(四) —— 内存管理(二)
About Memory Management - 关于内存管理
应用程序内存管理是在程序运行时分配内存的过程,使用它,并在完成后释放内存。 一个写得好的程序尽可能少地使用内存。 在Objective-C中,它也可以被看作是在许多数据和代码之间分配有限的内存资源的所有权的一种方式。 完成本指南后,通过明确管理对象的生命周期并在不再需要时释放它们,您将拥有管理应用程序内存所需的知识。
虽然通常将内存管理视为单个对象的级别,但您的目标实际上是管理object graphs.。 你想要确保你在内存中没有比实际需要更多的对象。
1. At a Glance - 概览
Objective-C提供了两种应用程序内存管理方法。
- 在本指南中描述的方法(称为
manual retain-release
)或MRR
中,通过跟踪您拥有的对象来明确管理内存。 这是使用基础类NSObject与运行时环境结合提供的模型(称为引用计数)来实现的。 - 在自动引用计数或ARC中,系统使用与MRR相同的引用计数系统,但在编译时会为您插入适当的内存管理方法调用。 强烈建议您使用ARC进行新项目。 如果您使用ARC,通常不需要了解本文档中描述的基础实现,尽管在某些情况下可能会有所帮助。 有关ARC的更多信息,请参阅Transitioning to ARC Release Notes。
Good Practices Prevent Memory-Related Problems - 良好做法防止与内存相关的问题
内存管理不正确导致的主要问题有两种:
- 释放或重写仍在使用的数据
- 这会导致内存损坏,并且通常会导致应用程序崩溃,甚至导致用户数据损坏。
- 不释放不再使用的数据会导致内存泄漏
- 内存泄漏是分配的内存不被释放的地方,尽管它再也不会被使用。泄漏导致您的应用程序使用不断增加的内存量,这反过来可能导致系统性能较差或您的应用程序被终止。
从引用计数的角度考虑内存管理往往适得其反,然而,因为您倾向于根据实施细节而不是实际目标考虑内存管理。相反,您应该从对象所有权和 object graphs的角度考虑内存管理。
Cocoa使用一个简单的命名约定来表示何时拥有一个方法返回的对象。
尽管基本策略是直截了当的,但您可以采取一些实际步骤,使管理内存更容易,并帮助确保程序保持可靠和稳健,同时最大限度地减少其资源需求。
请参阅Practical Memory Management。
自动释放池块提供了一种机制,您可以向对象发送deferred release
发布消息。这在您想要放弃对象的所有权的情况下很有用,但是希望避免立即释放它的可能性(例如从方法返回对象时)。有时您可能会使用自己的autorelease
池块。
请参阅Using Autorelease Pool Blocks。
Use Analysis Tools to Debug Memory Problems - 使用分析工具调试内存问题
为了在编译时识别代码的问题,可以使用Xcode中内置的Clang Static Analyzer
。如果出现内存管理问题,还可以使用其他工具和技术来识别和诊断问题。
- 技术说明
TN2239
,iOS Debugging Magic中的许多工具和技术都有描述,特别是使用NSZombie
来帮助找到过度释放的对象。 - 您可以使用
Instruments
跟踪引用计数事件并查找内存泄漏。 请参阅Collecting Data on Your App。
Memory Management Policy - 内存管理策略
用于引用计数环境中的内存管理的基本模型通过NSObject协议中定义的方法和标准方法命名约定的组合来提供。 NSObject类还定义了一个方法dealloc
,它在对象被释放时自动调用。 本文介绍了在Cocoa程序中正确管理内存所需的所有基本规则,并提供了一些正确使用的示例。
1. Basic Memory Management Rules - 基本内存管理准则
内存管理模型基于对象所有权。任何对象可能有一个或多个所有者。只要一个对象至少有一个所有者,它就会继续存在。如果对象没有所有者,则运行系统会自动破坏它。要确保什么时候您拥有对象时,什么时候你没有拥有,您可以设置以下策略:
- 你拥有你创建的任何对象
- 您可以使用名称以
“alloc”,“new”,“copy”或“mutableCopy”
(例如alloc,newObject或mutableCopy)开头的方法创建对象。
- 您可以使用名称以
- 您可以使用
retain
获取对象的所有权- 接收到的对象通常被保证在其被接收的方法内保持有效,并且该方法也可以安全地将对象返回给其调用者。您可以在两种情况下使用
retain
:(1)在实现访问器方法或init
方法时,将要存储为属性值的对象的所有权;(2)防止对象被无效作为某种其他操作的副作用(如Avoid Causing Deallocation of Objects You’re Using中所述)。
- 接收到的对象通常被保证在其被接收的方法内保持有效,并且该方法也可以安全地将对象返回给其调用者。您可以在两种情况下使用
- 当您不再需要它时,您必须放弃您拥有的对象的所有权
- 您通过发送一个release消息或autorelease消息来放弃对象的所有权。在Cocoa术语中,放弃对象的所有权因此通常被称为
releasing
对象。
- 您通过发送一个release消息或autorelease消息来放弃对象的所有权。在Cocoa术语中,放弃对象的所有权因此通常被称为
- 你不能放弃你不拥有的对象的所有权
- 这仅仅是以前的政策规则的推论。
A Simple Example - 一个简单的例子
为了说明策略,请考虑以下代码片段:
{
Person *aPerson = [[Person alloc] init];
// ...
NSString *name = aPerson.fullName;
// ...
[aPerson release];
}
使用alloc
方法创建Person
对象,因此在不再需要时发送release
消息。 该person的name不使用任何拥有的方法检索,因此不会发送release消息。 请注意,该示例使用release
而不是autorelease
。
Use autorelease to Send a Deferred release - 使用自动释放发送释放
当您需要发送延迟release
消息时,通常在从方法返回对象时使用autorelease
。 例如,您可以像这样实现fullName
方法:
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
self.firstName, self.lastName] autorelease];
return string;
}
你拥有由alloc
返回的字符串。 要遵守内存管理规则,您必须先放弃该字符串的所有权,然后再丢失该引用。 但是,如果您使用release
,则字符串将在返回之前被释放(并且该方法将返回无效对象)。 使用autorelease
,您表示您要放弃所有权,但您允许该方法的调用者在释放之前使用返回的字符串。
你也可以这样实现fullName
方法:
- (NSString *)fullName {
NSString *string = [NSString stringWithFormat:@"%@ %@",
self.firstName, self.lastName];
return string;
}
遵循基本规则,您不拥有由stringWithFormat:
返回的字符串,因此您可以安全地从方法返回字符串。
作为对比,以下实现是错误的:
- (NSString *)fullName {
NSString *string = [[NSString alloc] initWithFormat:@"%@ %@",
self.firstName, self.lastName];
return string;
}
根据命名约定,没有什么可以表示fullName
方法的调用者拥有返回的字符串。 因此,调用者没有理由释放返回的字符串,因此将被泄漏。
You Don’t Own Objects Returned by Reference - 您没有拥有引用返回的对象
Cocoa中的一些方法指定通过引用返回一个对象(也就是说,它们接受ClassName **
或id *
类型的参数)。 一个常见的模式是使用一个NSErro
r对象,其中包含有关错误的信息,如initWithContentsOfURL:options:error:(NSData)
和initWithContentsOfFile:encoding:error:(NSString)
所示。
在这些情况下,相同的规则适用于已经描述的规则。 当您调用任何这些方法时,您不会创建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];
2. Implement dealloc to Relinquish Ownership of Objects - 实现dealloc放弃对象的所有权
NSObject类定义了一个方法dealloc,当一个对象没有所有者,并且它的内存被回收时,它被自动调用 - 在Cocoa术语中它被freed
或deallocated
。 dealloc方法的作用是释放对象自己的内存,并处理其拥有的任何资源,包括任何对象实例变量的所有权。
以下示例说明如何为Person类实现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方法。
在执行结束时,必须调用超类的实现。
您不应将系统资源的管理与对象生命周期相结合; 请参阅Don’t Use dealloc to Manage Scarce Resources。
当应用程序终止时,对象可能不会被发送一个dealloc消息。 因为进程的内存在退出时自动清除,所以简单地说,允许操作系统清理资源比调用所有内存管理方法更为有效。
3. Core Foundation Uses Similar but Different Rules - Core Foundation使用相似但不同的规则
Core Foundation对象有类似的内存管理规则(请参阅Memory Management Programming Guide for Core Foundation)。 然而,Cocoa
和Core Foundation
的命名约定是不同的。 特别是,Core Foundation的创建规则(请参阅The Create Rule)不适用于返回Objective-C对象的方法。 例如,在以下代码片段中,您不负责放弃myInstance
的所有权:
MyClass *myInstance = [MyClass createInstance];
Practical Memory Management - 实用内存管理
虽然Memory Management Policy中描述的基本概念很简单,但您可以采取一些实际步骤,使管理内存更容易,并帮助确保程序保持可靠和稳健,同时最大限度地减少其资源需求。
1. Use Accessor Methods to Make Memory Management Easier - 使用访问方法使内存管理更轻松
如果您的类具有作为对象的属性,则必须确保在使用时将设置为该值的任何对象未被释放。 因此,您必须在设置对象时声明对象的所有权。 您还必须确保您放弃任何当前持有的价值的所有权。
有时候看起来很麻烦或迂腐,但是如果您一直使用访问器方法,那么存储管理问题的机会就会大大降低。 如果您在代码中使用retain
和release
实例变量,您几乎肯定会做错事。
考虑一个计数器对象,其数目要设置。
@interface Counter : NSObject
@property (nonatomic, retain) NSNumber *count;
@end;
该属性声明两个访问器方法。 通常,您应该要求编译器合成方法;然而,看看如何实现它们是有启发意义的。
在get
访问器中,只返回合成的实例变量,因此不需要retain
或release
:
- (NSNumber *)count {
return _count;
}
在set
方法中,如果其他所有人都按照相同的规则玩,你必须假定新的计数可能随时被处置,所以你必须拥有该对象的所有权 - 通过发送一个retain
消息,以确保它不会被释放,您还必须通过发送release
消息来放弃旧计数对象的所有权。 (在Objective-C中允许发送一个消息到nil,所以如果_count
还没有设置,执行将仍然可以工作。),你必须在[newCount retain]
之后发送,以防两个是相同对象 - 不想无意中导致它被释放。
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[_count release];
// Make the new assignment.
_count = newCount;
}
Use Accessor Methods to Set Property Values - 使用访问方法设置属性值
假设你想实现一个方法来重置计数器。 你有几个选择。 第一个实现使用alloc
创建NSNumber
实例,因此您可以使用release
保持平衡。
- (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[self setCount:zero];
[zero release];
}
第二个使用一个方便的构造函数来创建一个新的NSNumber
对象。 因此,不需要retain
或release
消息。
- (void)reset {
NSNumber *zero = [NSNumber numberWithInteger:0];
[self setCount:zero];
}
请注意,两者都使用set accessor
方法。
以下几乎肯定能够在简单的情况下正常工作,但是尽可能避免访问者的方法,这样做几乎肯定在某个阶段会导致错误(例如,当你忘记retain
或release
,或者如果内存管理语义为实例变量改变)。
- (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[_count release];
_count = zero;
}
还要注意,如果您使用key-value observing,则以这种方式更改变量不符合KVO。
Don’t Use Accessor Methods in Initializer Methods and dealloc - 不要在初始化程序方法和dealloc中使用Accessor方法
唯一不应该使用访问器方法来设置实例变量的地方在initializer
方法和dealloc
中。 要初始化一个counter
对象为零的计数器对象,可以按如下方式实现init
方法:
- init {
self = [super init];
if (self) {
_count = [[NSNumber alloc] initWithInteger:0];
}
return self;
}
为了允许使用不为零的counter
初始化计数器,可以按如下方式实现initWithCount:
方法:
- initWithCount:(NSNumber *)startingCount {
self = [super init];
if (self) {
_count = [startingCount copy];
}
return self;
}
由于Counter
类有一个对象实例变量,所以你必须实现一个dealloc
方法。 它应该放弃任何实例变量的所有权,通过发送一个release
消息,最终应该调用super的实现:
- (void)dealloc {
[_count release];
[super dealloc];
}
2. Use Weak References to Avoid Retain Cycles - 使用弱引用避免引用循环
保留对象将创建对该对象的强引用。 一个对象在其所有强引用被释放之前都不能被释放。 因此,如果两个对象可能具有循环引用,就会产生一个称为retain cycle
的问题 - 即它们彼此有很强的引用(直接地或者通过其他对象的链,每一个都有强烈的引用到下一个,又与第一个形成闭环)。
Figure 1所示的对象关系说明了潜在的retain cycle
。 Document
对象对于文档中的每个页面都有一个Page
对象。 每个Page
对象都有一个属性,可以跟踪它所在的文档。如果Document对象具有对Page对象的强烈引用,并且Page对象具有对Document对象的强烈引用,那么这两个对象都不能被释放。 在Document对象被释放之前,Document的引用计数不能变为零,并且在释放Document对象之前不会释放Page对象。
retain cycles
问题的解决方案是使用弱引用。弱引用是不拥有的关系,其中源对象不保留其具有引用的对象。
然而,为了保持对象图形的完整性,在某处必须有很强的引用(如果只有弱引用,那么页面和段落可能没有任何所有者,因此将被释放)。Cocoa
可以建立一个公约,因此,一个parent
对象应该保持对children
的强引用,而且孩子们对他们的父母类应该保持弱引用。
因此,在Figure 1中,文档对象document
具有强引用(保留)其Page
对象,但是Page
对象具有对(不保留)文档对象的弱引用。
Cocoa中弱引用的示例包括但不限于表数据源,table data sources
, outline view items
, notification observers, 和 miscellaneous targets
和 delegates。
您需要十分小心向仅保存弱引用的对象发送消息。如果您在释放对象后发送消息,您的应用程序将崩溃。当对象有效时,您必须具有明确的条件。在大多数情况下,弱引用的对象知道另一个对象的弱引用,如循环引用的情况,并且负责在释放另一个对象时进行通知。例如,当您在通知中心注册对象时,通知中心存储对该对象的弱引用,并在发布相应的通知时向其发送消息。当对象被释放时,您需要使用通知中心取消注册,以防止通知中心向对象发送任何不再存在的消息。同样,当代理对象被释放时,您需要通过向另一个对象发送一个带有nil
参数的setDelegate:
消息来删除委托链接。这些消息通常是从对象的dealloc
方法发送的。
3. Avoid Causing Deallocation of Objects You’re Using - 避免导致您正在使用的对象释放
Cocoa的所有权策略指定接收的对象通常在调用方法的整个范围内保持有效。 也可以从当前范围返回接收到的对象,而不用担心它被释放。 对应用程序来说,对象的getter
方法返回一个缓存的实例变量或一个计算的值是不重要的。 重要的是,该对象在您需要时保持有效。
这个规则偶尔有例外,主要分为两类。
- 当一个对象从基础collection classes中移除时。
heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.
当一个对象从一个基本的集合类中删除时,它被发送一个release
(而不是autorelease
)消息。 如果集合是已删除对象的唯一所有者,则会立即释放已删除的对象(示例中的heisenObject
)。
- 当
parent object
释放时
id parent = <#create a parent object#>;
// ...
heisenObject = [parent child] ;
[parent release]; // Or, for example: self.parent = nil;
// heisenObject could now be invalid.
在某些情况下,您从另一个对象中检索对象,然后直接或间接释放父对象。 如果释放父项导致它被释放,并且父对象是该子节点的唯一所有者,那么该子节点(该示例中的heisenObject
)将同时被释放(假设发送一个release
而不是一个autorelease
消息在父的dealloc
方法中)。
为了防止这些情况,您在收到heisenObject
后,保留heisenObject,并在完成之后释放它。 例如:
heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// Use heisenObject...
[heisenObject release];
4. Don’t Use dealloc to Manage Scarce Resources - 不要使用dealloc来管理稀缺资源
在dealloc方法中,通常不应该管理诸如文件描述符,网络连接,缓冲区或缓存等稀缺资源。特别是,您不应该设计类,以便在您认为它将被调用时调用dealloc。调用dealloc可能会因为错误或应用程序崩溃而被延迟或回避。
相反,如果您有一个实例管理稀缺资源的类,则应该设计应用程序,以便您知道何时不再需要资源,然后可以告诉实例clean up
该点。你通常会释放这个实例,而dealloc
会遵循,但是如果没有的话,你不会遭受额外的问题。
如果您尝试在dealloc
之上捎带资源管理,可能会出现问题。例如:
-
object graph 拆卸的顺序依赖。
- 对象图拆除机制本质上是无序的。虽然您通常可能希望获得特定的顺序,但您正在引入脆弱性。如果一个对象意外地自动释放而不是被释放,例如,拆除顺序可能会改变,这可能会导致意想不到的结果。
- 不填补稀缺资源。
- 内存泄漏是应该修复的错误,但它们通常不会立即死机。但是,如果您希望稀缺的资源释放但是他们却不释放时,那么您可能遇到更严重的问题。例如,如果您的应用程序用尽了文件描述符,则用户可能无法保存数据。
- 清除正在错误的线程上执行的逻辑。
- 如果一个对象在一个意外的时间被自动释放,它将被释放在任何线程的自动释放池块,它恰好在其中。对于只应该从一个线程获取的资源,这可能很容易致命。
5. Collections Own the Objects They Contain - 集合拥有它们包含的对象
当您将对象添加到集合(如数组,字典或集合)时,集合将拥有该集合的所有权。 当对象从集合中移除或集合本身被释放时,集合将放弃所有权。 因此,例如,如果要创建数字数组,则可以执行以下操作之一:
NSMutableArray *array = <#Get a mutable array#>;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
NSNumber *convenienceNumber = [NSNumber numberWithInteger:i];
[array addObject:convenienceNumber];
}
在这种情况下,您没有调用alloc
,所以不需要调用release
。 没有必要retain
新数字(convenienceNumber)
,因为数组将这样做。
NSMutableArray *array = <#Get a mutable array#>;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger:i];
[array addObject:allocedNumber];
[allocedNumber release];
}
在这种情况下,您需要在for循环的范围内发送allocedNumber
释放消息以平衡alloc
。 由于数组在addObject
添加时retain number
,所以在数组中不会释放它。
要理解这一点,把自己置于执行收集类的person
的位置。 你想确保没有给你的对象从你的下面消失,所以你传送给他们retain
消息,如果它们被删除,你必须发送一个平衡release
消息, 并且任何存在的对象应在您自己的dealloc方法期间发送release
消息。
6. Ownership Policy Is Implemented Using Retain Counts - 使用Retain Counts实现所有权策略
所有权政策通过引用计数来实现,通常在retain
方法之后称为retain count
。 每个对象都有一个保留计数。
- 创建对象时,它的保留计数为1。
- 当您向对象发送
retain
消息时,其保留计数将递增1。 - 当您向对象发送
release
消息时,其保留计数将递减1。 - 当您向对象发送
autorelease
消息时,其引用计数在当前自动释放池块的末尾递减1。 - 如果对象的引用计数减少为零,则将其释放。
重要:应该没有不需要明确的说明一个对象它的保留计数是什么(参见retainCount)。 结果通常是误导性的,因为您可能不知道框架对象是否保留了您感兴趣的对象。 在调试内存管理问题时,您应该注意确保代码遵守所有权规则。
Using Autorelease Pool Blocks - 使用自动释放池块
自动释放池块提供了一种机制,您可以放弃对象的所有权,但避免立即释放它的可能性(例如从方法返回对象时)。 通常,您不需要创建自己的自动释放池块,但是有一些情况,您必须或有必要这样做。
1. About Autorelease Pool Blocks - 关于自动释放池块
使用@autoreleasepool
标记自动释放池块,如以下示例所示:
@autoreleasepool {
// Code that creates autoreleased objects.
}
在自动释放池块的末尾,在块内接收到自动释放消息的对象被发送一个release
消息 - 对象每次在块内发送一个autorelease
消息时都会收到一个release
消息。
像任何其他代码块一样,自动释放池块可以嵌套:
@autoreleasepool {
// . . .
@autoreleasepool {
// . . .
}
. . .
}
(通常您通常不会像上面那样看到代码,通常,一个源文件中的自动释放池块中的代码将调用另一个自动释放池块中包含的另一个源文件中的代码。)对于给定的autorelease
消息,相应的release
消息在自动释放池块的末尾发送autorelease
消息时被发送。
Cocoa总是期望在自动释放池块中执行代码,否则自动释放的对象不会被释放,并且应用程序泄漏内存。 (如果您在自动释放池块之外发送autorelease
消息,Cocoa会记录一个合适的错误消息。)AppKit
和UIKit
框架处理自动释放池块中的每个事件循环迭代(例如鼠标向下事件或点击)。因此,您通常不必自己创建自动释放池块,甚至可以看到用于创建自动释放池块的代码。但是,有三种情况可能会使用您自己的autorelease
池块:
- 如果您正在编写一个不基于UI框架的程序,例如命令行工具。
- 如果你写一个创建许多临时对象的循环。
- 您可以在循环中使用自动释放池块在下一次迭代之前处理这些对象。在循环中使用自动释放池块有助于减少应用程序的最大内存占用。
- 如果你产生一个二级线程。
- 一旦线程开始执行,您必须创建自己的自动释放池块;否则,您的应用程序将泄漏对象。 (有关详细信息,请参阅Autorelease Pool Blocks and Threads 。)
2. Use Local Autorelease Pool Blocks to Reduce Peak Memory Footprint - 使用本地自动释放池块来减少峰值内存占用
许多程序创建自动释放的临时对象。 这些对象增加了程序的内存占用空间,直到块的结尾。 在许多情况下,允许临时对象累积直到当前事件循环迭代结束不会导致过多的开销;但是在某些情况下,您可能会创建大量临时对象,从而大大增加内存占用空间,并希望更快地处理它们。 在后一种情况下,您可以创建自己的自动释放池块。 在块的末尾,临时对象被释放,这通常导致其释放,从而减少程序的内存占用。
以下示例显示如何在for循环中使用本地自动释放池块。
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding error:&error];
/* Process the string, creating and autoreleasing more objects. */
}
}
for循环一次处理一个文件。 在自动释放池块中发送autorelease
消息的任何对象(如fileContents)在块的末尾释放。
在自动释放池块之后,您应该将块内自动释放的任何对象视为disposed of
。不要向该对象发送消息或将其返回给方法的调用者。 如果您必须使用超出自动释放池块的临时对象,则可以通过向块中的对象发送retain
消息,然后在块之后发送autorelease
,如本示例所示:
– (id)findMatchingObject:(id)anObject {
id match;
while (match == nil) {
@autoreleasepool {
/* Do a search that creates a lot of temporary objects. */
match = [self expensiveSearchForObject:anObject];
if (match != nil) {
[match retain]; /* Keep match around. */
}
}
}
return [match autorelease]; /* Let match go and return it. */
}
在autorelease pool
块中发送retain
去match
,并在自动释放池块扩展match
的生命周期后发送autorelease
,并允许它在循环之外接收消息,并返回给findMatchingObject:
的调用者。
3. Autorelease Pool Blocks and Threads - 自动释放池块和线程
Cocoa应用程序中的每个线程都维护自己的堆栈自动释放池块。如果您正在编写仅基础程序或分离线程,则需要创建自己的自动释放池块。
如果您的应用程序或线程长期存在,并且可能会生成大量自动释放的对象,则应使用自动释放池块(如主线程上的AppKit和UIKit);否则,自动释放的对象会累积,并且您的内存占用将增长。如果您的分离线程不会使Cocoa调用,则不需要使用自动释放池块。
注意:如果使用POSIX
线程API而不是NSThread
创建辅助线程,则除非Cocoa处于多线程模式,否则不能使用Cocoa。 Cocoa仅在分离其第一个NSThread
对象后才进入多线程模式。要在辅助POSIX线程上使用Cocoa,您的应用程序必须首先分离至少一个可以立即退出的NSThread对象。您可以使用NSThread
类方法isMultiThreaded来测试Cocoa是否处于多线程模式。
后记
未完,待续~~~