Apple - Advanced Memory Management Programming Guide 内存管理

翻译整理自: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提供了两种应用程序内存管理方法。

  1. 在本指南中描述的方法中,称为“手动保留释放”或MRR,您通过跟踪您拥有的对象来显式管理内存。
    这是使用一个称为引用计数的模型来实现的,该模型由Foundation类NSObject与运行时环境一起提供。

  2. 在自动引用计数或ARC中,系统使用与MRR相同的引用计数系统,但它会在编译时为您插入适当的内存管理方法调用。
    强烈建议您在新项目中使用ARC。
    如果您使用ARC,通常不需要了解本文档中描述的底层实现,尽管在某些情况下它可能会有所帮助。
    有关ARC的更多信息,请参阅*过渡到ARC发行说明*。


良好实践可防止与内存相关的问题

不正确的内存管理会导致两种主要问题:

  • 释放或覆盖仍在使用的数据
    这会导致内存损坏,通常会导致应用程序崩溃,或者更糟糕的是,损坏用户数据。
  • 不释放不再使用的数据会导致内存泄漏
    内存泄漏是指分配的内存没有被释放,即使它再也不会被使用。
    泄漏会导致应用程序使用越来越多的内存,进而可能导致系统性能不佳或应用程序被终止。

然而,从引用计数的角度考虑内存管理往往会适得其反,因为您倾向于根据实现细节而不是实际目标来考虑内存管理。
相反,您应该从对象所有权和对象图的角度来考虑内存管理。

Cocoa使用简单的命名约定来指示您何时拥有方法返回的对象。

请参见内存管理策略。

尽管基本策略很简单,但您可以采取一些实际步骤来简化内存管理,并帮助确保您的程序保持可靠和健壮,同时最大限度地减少其资源需求。

请参阅实用内存管理。

自动释放池块提供了一种机制,您可以借此向对象发送“延迟”release消息。
这在您想放弃对象的所有权但又想避免它立即被释放的可能性的情况下很有用(例如,当您从方法返回对象时)。
有时您可能会使用自己的自动释放池块。

请参见使用自动释放池块。


使用分析工具调试内存问题

要在编译时识别代码的问题,您可以使用Xcode中内置的Clang静态分析器。

如果仍然出现内存管理问题,您可以使用其他工具和技术来识别和诊断问题。

  • 许多工具和技术描述在技术说明TN2239,iOS调试魔术,特别是使用NSZombie来帮助找到过度释放的对象。
  • 您可以使用Instruments跟踪引用计数事件并查找内存泄漏。
    请参阅在您的应用程序上收集数据。

二、内存管理策略

在引用计数环境中用于内存管理的基本模型由NSObject 协议中定义的方法和标准方法命名约定的组合提供。
NSObject类还定义了一个方法,即dealloc对象时自动调用的方法。
本文描述了在Cocoa程序中正确管理内存所需了解的所有基本规则,并提供了一些正确使用的示例。


1、基本内存管理规则

内存管理模型基于对象所有权。
任何对象都可能有一个或多个所有者。
只要一个对象至少有一个所有者,它就会继续存在。
如果一个对象没有所有者,运行时系统会自动销毁它。
为了确保明确何时拥有对象,何时不拥有,Cocoa设置以下策略:

  • 您拥有您创建的任何对象
    使用名称以"alloc"、“new”、"cop"或"mutableCopy"开头的方法(例如,allocnewObjectmutableCopy)创建对象。
  • 您可以使用保留获得对象的所有权
    接收到的对象通常保证在它接收到的方法中保持有效,该方法也可以安全地将对象返回给它的调用者。
    您可以在两种情况下使用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:fileNameencoding: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、使用访问器方法使内存管理更容易

如果你的类有一个属性是一个对象,你必须确保任何被设置为值的对象在你使用它时都没有被释放。
因此,你必须在设置对象时声明它的所有权。
你还必须确保你放弃了任何当前持有值的所有权。

有时这可能看起来很乏味或迂腐,但是如果你坚持使用访问器方法,内存管理出现问题的可能性会大大降低。
如果你在整个代码中使用实例变量的retainrelease,你几乎肯定做错了。

考虑一个要设置其计数的Counter对象。

@interface Counter : NSObject
@property (nonatomic, retain) NSNumber *count;
@end;

该属性声明了两个访问器方法。
通常,您应该要求编译器合成这些方法;但是,了解它们是如何实现的很有启发性。

在“get”访问器中,您只需返回合成的实例变量,因此无需retainrelease

- (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对象。
因此不需要retainrelease消息

- (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 方法是否返回缓存的实例变量或计算值并不重要。重要的是,对象在您需要它时仍然有效。

这条规则偶尔也有例外,主要分为以下两类。

  1. 对象从基本集合类中删除。
heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.

从基本集合类中移除对象时,会向其发送一条 release 消息(而不是 autorelease)。如果该集合是被移除对象的唯一所有者,那么被移除的对象(示例中的 heisenObject)就会立即被取消分配。


  1. 当一个 “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之上搭载资源管理,可能会出现问题。
例如:

  1. 顺序依赖于对象图拆解。
    对象图的拆解机制本质上是无序的。
    尽管您通常期望并获得特定的顺序,但您引入了脆弱性。
    例如,如果对象意外地自动释放而不是释放,则拆解顺序可能会改变,这可能会导致意外结果。
  2. 不回收稀缺资源。
    内存泄漏是应该修复的错误,但它们通常不会立即致命。
    但是,如果稀缺资源在您期望释放时没有释放,您可能会遇到更严重的问题。
    例如,如果您的应用程序用完了文件描述符,用户可能无法保存数据。
  3. 清理逻辑在错误的线程上执行。
    如果一个对象在意外时间被自动释放,它将在它碰巧所在的任何线程的自动释放池块上被释放。
    这对于应该只从一个线程触及的资源来说很容易是致命的。

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:urlencoding: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(日)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/32264.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

异步FIFO

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 参考代码 描述 请根据题目中给出的双口RAM代码和接口描述&#xff0c;实现异步FIFO&#xff0c;要求FIFO位宽和深度参数化可配置。 电路的接口如下图所示。 双口RAM端口说明&#xff1a; 端口名 I/O 描述 wclk i…

LQR 控制算法应用分析

参考 Optimization Based Control 从基础到复杂地介绍最优控制理论 麻省理工大学机器人算法第八章 LQR 大概说了 lqr 的推导过程&#xff0c;主页有更多算法介绍 wiki LQR 控制器 LQR 多种公式说明 Formulas for discrete time LQR, LQG, LEQG and minimax LQG optimal con…

Springboot项目ES报异常query_shard_exception

详细异常信息如下&#xff1a; {"error": {"root_cause": [{"type": "query_shard_exception","reason": "failed to create query: {\n \"bool\" : {\n \"filter\" : [\n {\n \…

换电脑后导入git本地仓库记录

导入本地仓库tig记录 换了新电脑&#xff0c;将旧电脑的数据盘查到新的笔记本之后发现&#xff0c;使用pycharm 读取不到本地的git提交记录了&#xff0c;我没有将本地git上传到远程仓库的习惯&#xff0c;这可抓马了&#xff0c;硬盘插回去的话也太麻烦了。试了 vscode 提示设…

Vue77-编程式路由

一、需求 不写<router-link>实行路由的跳转。 因为<router-link>的本质是<a>&#xff0c;但是&#xff0c;有时&#xff0c;导航不一定是a标签&#xff01;或者&#xff0c;有时需要等一段时间&#xff0c;页面才跳转。 二、代码实现 三、小结

Wakelocks 框架设计与实现

Wakelocks 框架是基于Wakeup Source实现的为Android系统上层提供投票机制&#xff0c;以阻止系统进入休眠。 1.功能说明 该模块的支持受宏CONFIG_PM_WAKELOCKS控制。在使能该宏的情况下&#xff0c;PM Core初始化过程中会在sysfs下创建两个属性节点&#xff1a; /sys/power/w…

Gradle学习-1

1、APK构建流程 2、Gradle的安装 &#xff08;1&#xff09;安装Java JDK JAVA JDK 下载地址下载安装后需要配置环境变量gradle是运行在Java虚拟机上的&#xff0c;所以需要配置Java JDK &#xff08;2&#xff09;安装 Gradle Gradle下载官网下载安装后需要配置环境变量 …

vscode创建编辑markdown文件

Markdown 是一种轻量级标记语言, 它允许人们使用易读易写的纯文本格式编写文档&#xff0c;然后转换成有效的 XHTML&#xff08;或者HTML&#xff09;文档。 由于 Markdown 的轻量化、易读易写特性&#xff0c;并且对于图片&#xff0c;图表、数学式都有支持&#xff0c;许多网…

[保姆级教程]uniapp配置vueX

文章目录 注意新建文件简单的使用 注意 uniapp是支持vueX的只需配置一下就好 新建文件 在src文件中&#xff0c;新建一个store&#xff08;如果有的话跳过&#xff09; 在store中新建一个js文件&#xff0c;修改js文件名称和选择模板为default 在 uni-app 项目根目录下&…

Vue80-全局路由守卫:前置、后置

一、路由守卫的定义 二、需求 在第三步&#xff0c;做校验&#xff01; 三、代码实现 3-1、前置路由守卫 注意&#xff0c;此时就不能将router一开始就暴露出去了&#xff01; to和from是路由组件的信息。 写法一&#xff1a; 写法二&#xff1a; 缺点&#xff1a;若是路由…

51单片机STC89C52RC——6.2 定时器

一&#xff0c;定时器介绍 STC89C51RC/RD系列单片机的定时器0和定时器1&#xff0c;与传统8051的定时器完全兼容&#xff0c;当在定时器1做波特率发生器时&#xff0c;定时器0可以当两个8位定时器用。 STC89C51RC/RD系列单片机内部设置的两个16位定时器/计数器TO和T1都…

“论数据访问层设计技术及其应用”必过范文,软考高级,系统架构设计师论文

论文真题 在信息系统的开发与建设中,分层设计是一种常见的架构设计方法,区分层次的目的是为了实现“高内聚低耦合”的思想。分层设计能有效简化系统复杂性,使设计结构清晰,便于提高复用能力和产品维护能力。一种常见的层次划分模型是将信息系统分为表现层、业务逻辑层和数…

laravel8框架windows下安装运行

目录 1、安装前如果未安装先安装Composer 2、使用composer安装laravel8 3、使用内置服务器:8000 的命令去访问测试 ​4、使用本地环境运行phpstudy配置到public目录下 Laravel官网 Laravel 中文网 为 Web 工匠创造的 PHP 框架 安装 | 入门指南 |《Laravel 8 中文文档 8.x…

[数据集][图像分类]瑜伽动作分类数据集1238张5类别

数据集类型&#xff1a;图像分类用&#xff0c;不可用于目标检测无标注文件 数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;1238 分类类别数&#xff1a;5 类别名称:["downdog","godde…

聊聊贪心算法

第1部分&#xff1a;引言 贪心算法是一种在每一步选择中都采取当前状态下最优&#xff08;或最有利&#xff09;的选择&#xff0c;从而希望导致结果是全局最优的算法策略。这种算法简单、直观&#xff0c;且在很多情况下能够快速得到一个足够好的解决方案。然而&#xff0c;值…

el-upload 组件上传文件(查询,上传,删除,下载功能)

1.html el-upload中的属性&#xff1a; <el-upload ref"upload" class"upload-demo" // element-ui自带的样式 :headers"headerOdj" // 文件上传的头,带token&#xff08;重要&#xff0c;不然传输大文件会断掉&…

Shell编程规范与变量-01

一、Shell脚本概述 在一些复杂的 Linux 维护工作中&#xff0c;大量重复性的输入和交互操作不仅费时费力&#xff0c;而且容易出错&#xff0c;而编写一个恰到好处的 Shell 脚本程序&#xff0c;可以批量处理、自动化地完成一系列维护任务&#xff0c;大大减轻管理员的负担。 1…

PyScada(三)后端应用

PyScada 的后端应用 使用后端 要使用后端&#xff0c;请在浏览器中打开http://127.0.0.1 &#xff08;将 127.0.0.1 替换为 PyScada 服务器的 IP 或主机名&#xff09;&#xff0c;然后使用安装过程 中定义的管理员帐户登录 &#xff08;TODO 链接到创建超级用户文档&#xf…

Java基础的重点知识-01

文章目录 开发前言Java语言开发环境入门程序说明常量变量和数据类型数据类型转换运算符方法解析 开发前言 常用DOS命令 Java语言的初学者&#xff0c;学习一些DOS命令&#xff0c;会非常有帮助。DOS是一个早期的操作系统&#xff0c;现在已经被Windows系统取代&#xff0c;对于…

Vue2动态代理无须重启项目解决方案

1、痛点 如果我们需要使用不同的环境地址的时候&#xff0c;就需要使用命令或者手动修改vue.config.js中配置来重新启动项目。当项目项目越来越大的时候&#xff0c;我们需要很长的时间来启动项目&#xff0c;如此反复&#xff0c;极大影响我们开发进度。 2、寻求解决方案 ● v…