翻译整理自:Advanced Memory Management Programming Guide(Updated: 2012-07-17
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html#//apple_ref/doc/uid/10000011i
文章目录
- 一、关于内存管理
- 概览
- 良好实践可防止与内存相关的问题
- 使用分析工具调试内存问题
- 二、内存管理策略
- 1、基本内存管理规则
- 一个简单的例子
- 使用自动发布发送延迟发布
- 您不拥有通过引用返回的对象
- 2、实现deloc以放弃对象的所有权
- 3、Core Foundation使用相似但不同的规则
- 三、实用内存管理
- 1、使用访问器方法使内存管理更容易
- 使用访问器方法设置属性值
- 不要在初始化方法和释放中使用访问器方法
- 2、使用弱引用来避免保留周期
- 3、避免导致您正在使用的对象被取消分配
- 4、不要使用deloc来管理稀缺资源
- 5、集合拥有它们包含的对象
- 6、使用保留计数实施所有权政策
- 四、使用自动释放池块
- 1、关于自动释放池块
- 2、使用本地自动释放池块来减少峰值内存占用
- 3、自动释放池块和线程
一、关于内存管理
应用程序内存管理是在程序运行时分配内存,使用它,并在完成后释放它的过程。
一个编写良好的程序使用尽可能少的内存。
在Objective-C中,它也可以被视为在许多数据和代码之间分配有限内存资源所有权的一种方式。
当你完成本指南的工作后,你将拥有管理应用程序内存所需的知识,通过显式管理对象的生命周期并在不再需要时释放它们。
尽管内存管理通常是在单个对象的级别上考虑的,但您的目标实际上是管理对象图。
您要确保内存中的对象没有超过实际需要。
概览
Objective-C提供了两种应用程序内存管理方法。
-
在本指南中描述的方法中,称为“手动保留释放”或MRR,您通过跟踪您拥有的对象来显式管理内存。
这是使用一个称为引用计数的模型来实现的,该模型由Foundation类NSObject
与运行时环境一起提供。 -
在自动引用计数或ARC中,系统使用与MRR相同的引用计数系统,但它会在编译时为您插入适当的内存管理方法调用。
强烈建议您在新项目中使用ARC。
如果您使用ARC,通常不需要了解本文档中描述的底层实现,尽管在某些情况下它可能会有所帮助。
有关ARC的更多信息,请参阅*过渡到ARC发行说明*。
良好实践可防止与内存相关的问题
不正确的内存管理会导致两种主要问题:
- 释放或覆盖仍在使用的数据
这会导致内存损坏,通常会导致应用程序崩溃,或者更糟糕的是,损坏用户数据。 - 不释放不再使用的数据会导致内存泄漏
内存泄漏是指分配的内存没有被释放,即使它再也不会被使用。
泄漏会导致应用程序使用越来越多的内存,进而可能导致系统性能不佳或应用程序被终止。
然而,从引用计数的角度考虑内存管理往往会适得其反,因为您倾向于根据实现细节而不是实际目标来考虑内存管理。
相反,您应该从对象所有权和对象图的角度来考虑内存管理。
Cocoa使用简单的命名约定来指示您何时拥有方法返回的对象。
请参见内存管理策略。
尽管基本策略很简单,但您可以采取一些实际步骤来简化内存管理,并帮助确保您的程序保持可靠和健壮,同时最大限度地减少其资源需求。
请参阅实用内存管理。
自动释放池块提供了一种机制,您可以借此向对象发送“延迟”release
消息。
这在您想放弃对象的所有权但又想避免它立即被释放的可能性的情况下很有用(例如,当您从方法返回对象时)。
有时您可能会使用自己的自动释放池块。
请参见使用自动释放池块。
使用分析工具调试内存问题
要在编译时识别代码的问题,您可以使用Xcode中内置的Clang静态分析器。
如果仍然出现内存管理问题,您可以使用其他工具和技术来识别和诊断问题。
- 许多工具和技术描述在技术说明TN2239,iOS调试魔术,特别是使用
NSZombie
来帮助找到过度释放的对象。 - 您可以使用Instruments跟踪引用计数事件并查找内存泄漏。
请参阅在您的应用程序上收集数据。
二、内存管理策略
在引用计数环境中用于内存管理的基本模型由NSObject
协议中定义的方法和标准方法命名约定的组合提供。
NSObject
类还定义了一个方法,即dealloc
对象时自动调用的方法。
本文描述了在Cocoa程序中正确管理内存所需了解的所有基本规则,并提供了一些正确使用的示例。
1、基本内存管理规则
内存管理模型基于对象所有权。
任何对象都可能有一个或多个所有者。
只要一个对象至少有一个所有者,它就会继续存在。
如果一个对象没有所有者,运行时系统会自动销毁它。
为了确保明确何时拥有对象,何时不拥有,Cocoa设置以下策略:
- 您拥有您创建的任何对象
使用名称以"alloc"、“new”、"cop"或"mutableCopy"开头的方法(例如,alloc
、newObject
或mutableCopy
)创建对象。 - 您可以使用保留获得对象的所有权
接收到的对象通常保证在它接收到的方法中保持有效,该方法也可以安全地将对象返回给它的调用者。
您可以在两种情况下使用retain
:(1)在访问器方法或init
方法的实现中,获取要作为属性值存储的对象的所有权;(2)防止对象因其他操作的副作用而失效(如避免导致您正在使用的对象的解除分配)。 - 当您不再需要它时,您必须放弃对您拥有的对象的所有权
通过向对象发送release
消息或autorelease
消息来放弃对象的所有权。
因此,在Cocoa术语中,放弃对象的所有权通常称为“释放”对象。 - 您不得放弃您不拥有的对象的所有权
这只是以前明确规定的政策规则的必然结果。
一个简单的例子
为了说明该策略,请考虑以下代码片段:
{
Person *aPerson = [[Person alloc] init];
// ...
NSString *name = aPerson.fullName;
// ...
[aPerson release];
}
Person对象是使用alloc
方法创建的,因此当不再需要它时,它会随后发送release
消息。
没有使用任何owning方法检索人员的姓名,因此不会发送release
消息。
但是请注意,该示例使用的是release
而不是autorelease
。
使用自动发布发送延迟发布
当您需要发送延迟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
方法的调用者拥有返回的字符串。
因此,调用者没有理由释放返回的字符串,因此它将被泄露。
您不拥有通过引用返回的对象
Cocoa中的一些方法指定通过引用返回对象(也就是说,它们采用ClassName **
或id *
类型的参数)。
一种常见的模式是使用NSError
对象,该对象包含错误发生时的信息,如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、实现deloc以放弃对象的所有权
在NSObject
类中定义了一个方法,即dealloc
,当一个对象没有所有者并且它的内存被回收时,它会自动调用——在Cocoa术语中,它是“释放的”或“释放的”。
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
方法。
您必须在实现结束时调用超类的实现。
您不应该将系统资源的管理与对象的生命周期联系起来;请参阅不要使用释放来管理稀缺资源。
当应用程序终止时,对象可能不会被发送dealloc
消息。
因为进程的内存在退出时会自动清除,所以简单地允许操作系统清理资源比调用所有内存管理方法更有效。
3、Core Foundation使用相似但不同的规则
Core Foundation对象也有类似的内存管理规则(请参阅*Core Foundation的内存管理编程指南)。
然而,Cocoa和Core Foundation的命名约定是不同的。
特别是,Core Foundation的创建规则(请参阅创建规则)不适用于返回Objective-C对象的方法。
例如,在以下代码片段中,您不*负责放弃myInstance
的所有权:
MyClass *myInstance = [MyClass createInstance];
三、实用内存管理
尽管内存管理策略中描述的基本概念很简单,但您可以采取一些实际步骤来简化内存管理,并帮助确保您的程序保持可靠和健壮,同时最大限度地减少其资源需求。
1、使用访问器方法使内存管理更容易
如果你的类有一个属性是一个对象,你必须确保任何被设置为值的对象在你使用它时都没有被释放。
因此,你必须在设置对象时声明它的所有权。
你还必须确保你放弃了任何当前持有值的所有权。
有时这可能看起来很乏味或迂腐,但是如果你坚持使用访问器方法,内存管理出现问题的可能性会大大降低。
如果你在整个代码中使用实例变量的retain
和release
,你几乎肯定做错了。
考虑一个要设置其计数的Counter对象。
@interface Counter : NSObject
@property (nonatomic, retain) NSNumber *count;
@end;
该属性声明了两个访问器方法。
通常,您应该要求编译器合成这些方法;但是,了解它们是如何实现的很有启发性。
在“get”访问器中,您只需返回合成的实例变量,因此无需retain
或release
:
- (NSNumber *)count {
return _count;
}
在“set”方法中,如果其他人都遵循相同的规则,您必须假设新的计数可能随时被丢弃,因此您必须获得对象的所有权——通过向其发送retain
消息——以确保它不会被丢弃。
您还必须通过向其发送release
消息来放弃旧count对象的所有权。
(在Objective-C中允许向nil
发送消息,因此如果_count
尚未设置。)您必须在之后发送此消息[newCount retain]
以防这两个是同一个对象——您不想无意中导致它被释放。
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[_count release];
// Make the new assignment.
_count = newCount;
}
使用访问器方法设置属性值
假设您想实现一个重置计数器的方法。
您有几个选择。
第一个实现使用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访问器方法。
下面的方法几乎肯定适用于简单的情况,但是避免访问器方法可能很诱人,这样做几乎肯定会在某个阶段导致错误(例如,当您忘记保留或释放时,或者如果实例变量的内存管理语义学发生变化)。
- (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[_count release];
_count = zero;
}
另请注意,如果您使用键值观察,那么以这种方式更改变量不符合KVO。
不要在初始化方法和释放中使用访问器方法
唯一不应该使用访问器方法来设置实例变量的地方是初始化程序方法和dealloc
。
要使用表示零的数字对象初始化计数器对象,您可以实现如下的init
方法:
- init {
self = [super init];
if (self) {
_count = [[NSNumber alloc] initWithInteger:0];
}
return self;
}
要允许使用非零计数初始化计数器,您可以实现initWithCount:
方法,如下所示:
- initWithCount:(NSNumber *)startingCount {
self = [super init];
if (self) {
_count = [startingCount copy];
}
return self;
}
由于计数器类有一个对象实例变量,您还必须实现一个dealloc
方法。
它应该通过向任何实例变量发送release
消息来放弃它们的所有权,最终它应该调用超级的实现:
- (void)dealloc {
[_count release];
[super dealloc];
}
2、使用弱引用来避免保留周期
保留一个对象会创建对该对象的强引用。
在释放所有强引用之前,不能释放一个对象。
因此,如果两个对象可能具有循环引用,则会出现一个称为保留循环的问题——也就是说,它们彼此具有强引用(直接引用,或者通过其他对象的链,每个对象都具有对下一个对象的强引用,从而返回到第一个对象)。
如[图1] 所示的对象关系说明了一个潜在的保留周期。
Document对象对文档中的每个页面都有一个Page对象。
每个Page对象都有一个属性来跟踪它在哪个文档中。
如果Document对象对Page对象有一个强引用,而Page对象对Document对象有一个强引用,则两个对象都不能被释放。
在Page对象被释放之前,Document的引用计数不能变为零,在Document对象被释放之前,Page对象不会被释放。
图1循环参考的图示
保留循环问题的解决方案是使用弱引用。
弱引用是一种非拥有关系,其中源对象不保留它具有引用的对象。
然而,为了保持对象图的完整,必须在某个地方有强引用(如果只有弱引用,那么页面和段落可能没有任何所有者,因此将被释放)。
因此,Cocoa建立了一个约定,即“父”对象应该保持对其“子”的强引用,而子对象应该对其父对象有弱引用。
因此,在图1中,文档对象对其页面对象具有强引用(保留),但页面对象对文档对象具有弱引用(不保留)。
Cocoa中弱引用的示例包括但不限于表数据源、大纲视图项、通知观察者以及其他目标和委托。
在向仅持有弱引用的对象发送消息时,您需要小心。
如果您在对象被解除分配后向其发送消息,您的应用程序将崩溃。
您必须对对象何时有效有明确定义的条件。
在大多数情况下,弱引用对象会意识到另一个对象对它的弱引用,循环引用就是这种情况,并负责在它解除分配时通知另一个对象。
例如,当您向通知中心注册一个对象时,通知中心会存储对该对象的弱引用,并在发布适当的通知时向其发送消息。
当对象被解除分配时,您需要在通知中心取消注册它,以防止通知中心向该对象发送任何进一步的消息,该对象已不存在。
同样,当一个委托对象被解除分配时,您需要通过向另一个对象发送带有nil
参数的setDelegate:
消息来删除委托链接。
这些消息通常从对象的dealloc
方法发送。
3、避免导致您正在使用的对象被取消分配
Cocoa 的所有权策略规定,接收对象通常应在调用方法的整个作用域内保持有效。
从当前作用域返回接收到的对象时,也不应担心该对象会被释放。对于您的应用程序来说,对象的 getter 方法是否返回缓存的实例变量或计算值并不重要。重要的是,对象在您需要它时仍然有效。
这条规则偶尔也有例外,主要分为以下两类。
- 对象从基本集合类中删除。
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
而不是父对象的dealloc
方法中的autorelease
消息)。
为了防止这些情况,您在收到heisenObject
时保留它,并在完成后释放它。
例如:
heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// Use heisenObject...
[heisenObject release];
4、不要使用deloc来管理稀缺资源
您通常不应该在dealloc
方法中管理稀缺资源,如文件描述符、网络连接和缓冲区或缓存。
特别是,您不应该设计类,以便在您认为将调用dealloc
时调用它。
dealloc
的调用可能会延迟或回避,要么是因为bug,要么是因为应用程序拆除。
相反,如果你有一个实例管理稀缺资源的类,你应该设计你的应用程序,这样你就知道什么时候不再需要资源,然后可以告诉实例在那个时候“清理”。
然后你通常会释放实例,dealloc
会随之而来,但是如果没有,你不会遇到额外的问题。
如果您试图在dealloc
之上搭载资源管理,可能会出现问题。
例如:
- 顺序依赖于对象图拆解。
对象图的拆解机制本质上是无序的。
尽管您通常期望并获得特定的顺序,但您引入了脆弱性。
例如,如果对象意外地自动释放而不是释放,则拆解顺序可能会改变,这可能会导致意外结果。 - 不回收稀缺资源。
内存泄漏是应该修复的错误,但它们通常不会立即致命。
但是,如果稀缺资源在您期望释放时没有释放,您可能会遇到更严重的问题。
例如,如果您的应用程序用完了文件描述符,用户可能无法保存数据。 - 清理逻辑在错误的线程上执行。
如果一个对象在意外时间被自动释放,它将在它碰巧所在的任何线程的自动释放池块上被释放。
这对于应该只从一个线程触及的资源来说很容易是致命的。
5、集合拥有它们包含的对象
当您将对象添加到集合(例如数组、字典或集合)时,集合将获得它的所有权。
当对象从集合中删除或集合本身被释放时,集合将放弃所有权。
因此,例如,如果您想创建一个数字数组,您可以执行以下操作之一:
NSMutableArray *array = <#Get a mutable array#>;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
NSNumber *convenienceNumber = [NSNumber numberWithInteger:i];
[array addObject:convenienceNumber];
}
在这种情况下,您没有调用alloc
,所以没有必要调用release
。
没有必要保留新数字(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];
}
在这种情况下,您确实需要在allocedNumber``for
循环的范围内发送release
消息来平衡alloc
。
由于数组在addObject:
添加时保留了数字,因此它在数组中时不会被释放。
要理解这一点,请把自己放在实现集合类的人的位置上。
您想确保没有让您照顾的对象从您下面消失,因此您在它们传入时向它们发送一条retain
消息。
如果它们被删除,您必须发送一条平衡release
消息,任何剩余的对象都应该在您自己的dealloc
方法期间发送一条release
消息。
6、使用保留计数实施所有权政策
所有权策略是通过引用计数来实现的——通常在保留方法之后称为“retain
计数”。
每个对象都有一个保留计数。
- 当您创建一个对象时,它的保留计数为1。
- 当您向对象发送
retain
消息时,其保留计数将增加1。 - 当您向对象发送
release
消息时,它的保留计数将减少1。
当您向对象发送autorelease
消息时,它的保留计数在当前自动释放池块的末尾减1。 - 如果对象的保留计数减少到零,则释放它。
**重要提示:**应该没有理由明确询问对象的保留计数是多少(请参阅retainCount
)。
结果通常会产生误导,因为您可能不知道哪些框架对象保留了您感兴趣的对象。
在调试内存管理问题时,您应该只关心确保您的代码符合所有权规则。
四、使用自动释放池块
自动释放池块提供了一种机制,通过这种机制,您可以放弃对象的所有权,但避免它立即被释放的可能性(例如,当您从方法返回对象时)。
通常,您不需要创建自己的自动释放池块,但在某些情况下,您必须这样做或者这样做是有益的。
1、关于自动释放池块
自动释放池块使用@autoreleasepool
进行标记,如下例所示:
@autoreleasepool {
// Code that creates autoreleased objects.
}
在自动释放池块的末尾,在块内接收到autorelease
消息的对象被发送release
消息——每次在块内发送autorelease
消息时,对象都会收到release
消息。
与任何其他代码块一样,自动释放池块可以嵌套:
@autoreleasepool {
// . . .
@autoreleasepool {
// . . .
}
. . .
}
(您通常不会看到与上述完全相同的代码;通常,一个源文件中自动释放池块中的代码将调用另一个源文件中包含在另一个自动释放池块中的代码。)对于给定的autorelease
消息,相应的release
消息将在发送autorelease
消息的自动释放池块的末尾发送。
Cocoa总是期望代码在自动释放池块中执行,否则自动释放的对象不会被释放,您的应用程序会泄漏内存。
(如果您在自动释放池块之外发送autorelease
消息,Cocoa会记录一条合适的错误消息。)AppKit和UIKit框架处理自动释放池块中的每个事件循环迭代(例如鼠标向下事件或点击)。
因此,您通常不必自己创建自动释放池块,甚至不必查看用于创建自动释放池块的代码。
但是,您可以在三种情况下使用自己的自动释放池块:
- 如果您正在编写不基于UI框架的程序,例如命令行工具。
- 如果您编写一个创建许多临时对象的循环。
您可以在循环内使用自动释放池块来在下一次迭代之前处理这些对象。
在循环中使用自动释放池块有助于减少应用程序的最大内存占用。 - 如果您生成一个辅助线程。
您必须在线程开始执行后立即创建自己的自动释放池块;否则,您的应用程序将泄漏对象。
(有关详细信息,请参阅自动释放池块和线程。)
2、使用本地自动释放池块来减少峰值内存占用
许多程序创建自动释放的临时对象。
这些对象会增加程序的内存占用,直到块结束。
在许多情况下,允许临时对象累积到当前事件循环迭代结束不会导致过多的开销;但是,在某些情况下,您可能会创建大量临时对象,这些对象会大大增加内存占用,并且您希望更快地处理它们。
在后一种情况下,您可以创建自己的自动释放池块。
在块结束时,临时对象被释放,这通常会导致它们的释放,从而减少程序的内存占用。
下面的示例显示了如何在for循环中使用本地自动释放池块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
)都会在块的末尾释放。
在自动释放池块之后,您应该将块内自动释放的任何对象视为“已释放”。
不要向该对象发送消息或将其返回给方法的调用者。
如果您必须在自动释放池块之外使用临时对象,您可以通过向块内的对象发送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. */
}
发送retain
到自动释放池块内的match
,并在自动释放池块后发送autorelease
给它,延长了match
的生命周期,并允许它接收循环之外的消息并返回给findMatchingObject:
的调用者。
3、自动释放池块和线程
Cocoa应用程序中的每个线程都维护自己的自动释放池块堆栈。
如果您正在编写仅限Foundation的程序或分离线程,则需要创建自己的自动释放池块。
如果您的应用程序或线程是长期存在的,并且可能会生成大量自动释放的对象,您应该使用自动释放池块(就像主线程上的AppKit和UIKit一样);否则,自动释放的对象会累积,您的内存占用会增加。
如果您的分离线程不进行Cocoa调用,则不需要使用自动释放池块。
**注意:**如果您使用POSIX线程API而不是NSThread
创建辅助线程,则不能使用Cocoa,除非Cocoa处于多线程模式。
Cocoa只有在分离其第一个NSThread
对象后才能进入多线程模式。
要在辅助POSIX线程上使用Cocoa,您的应用程序必须首先分离至少一个NSThread
对象,该对象可以立即退出。
您可以使用NSThread
类方法isMultiThreaded
测试Cocoa是否处于多线程模式。
2024-06-16(日)