了解可可和Objective-C的参考计数
我刚刚开始研究Objective-C和Cocoa,以期使用iPhone SDK。我对C的malloc
和free
概念相当满意,但是Cocoa的引用计数方案使我感到困惑。有人告诉我它非常优雅,但是我还没有结束。
release,retain和autorelease如何工作,以及它们的使用约定是什么?
(或者失败了,我们读了什么书,对我们有所帮助?)
解决方案
回答
Objective-C使用引用计数,这意味着每个对象都有一个引用计数。创建对象时,其引用计数为" 1"。简单来说,当一个对象被引用(即存储在某处)时,它被"保留",这意味着其引用计数增加了一个。当不再需要某个对象时,该对象将被"释放",这意味着其引用计数将减少一。
当对象的引用计数为0时,将释放该对象。这是基本的参考计数。
对于某些语言,引用会自动增加和减少,但是Objective-C并不是这些语言之一。因此,程序员负责保留和释放。
编写方法的典型方法是:
id myVar = [someObject someMessage]; .... do something ....; [myVar release]; return someValue;
需要记住在代码内部释放任何获取的资源的问题既繁琐又容易出错。 Objective-C引入了另一个旨在简化这一过程的概念:自动释放池。自动释放池是安装在每个线程上的特殊对象。如果我们查找NSAutoreleasePool,它们是一个相当简单的类。
当对象收到发送给它的"自动释放"消息时,该对象将查找该当前线程在堆栈上的所有自动释放池。它将把对象作为一个对象添加到列表中,以便在将来某个时候(通常是在释放池本身时)向其发送"释放"消息。
通过上面的代码,我们可以通过说出以下代码将其重写为更短,更容易阅读的代码:
id myVar = [[someObject someMessage] autorelease]; ... do something ...; return someValue;
因为对象是自动释放的,所以我们不再需要在其上显式调用"释放"。这是因为我们知道一些自动释放池稍后会为我们完成此任务。
希望这会有所帮助。维基百科上有关引用计数的文章相当不错。有关自动释放池的更多信息,请参见此处。还要注意,如果要针对Mac OS X 10.5和更高版本进行构建,则可以告诉Xcode在启用垃圾收集的情况下进行构建,从而使我们可以完全忽略保留/释放/自动释放。
回答
Joshua(#6591)Mac OS X 10.5中的Garbage集合内容看起来很酷,但不适用于iPhone(或者如果我们希望应用程序在Mac OS X 10.5之前的版本上运行)。
另外,如果我们正在编写库或者可能被重用的内容,则使用GC模式会将使用代码的任何人锁定为也使用GC模式,因此据我所知,任何试图编写可广泛重用的代码的人都倾向于管理手动记忆。
回答
NilObject的答案是一个好的开始。这是与手动内存管理有关的一些补充信息(在iPhone上是必需的)。
如果我们亲自"分配/初始化"一个对象,则该对象的引用计数为1. 我们有责任在不再需要该对象时对其进行清理,方法是调用" [foo release]"或者" [foo autorelease]"。 。 release会立即清除它,而autorelease会将对象添加到autorelease池中,后者会在以后自动释放它。
自动释放主要用于当我们有一个需要返回有问题的对象的方法时(因此我们不能手动释放它,否则将返回一个nil对象),但又不想保留该对象。
如果获取的对象没有调用alloc / init来获取它,例如:
foo = [NSString stringWithString:@"hello"];
但我们想挂接到该对象,则需要调用[foo keep]。否则,它可能会被"自动释放",并且我们将坚持使用nil引用(就像上面的stringWithString示例中那样)。当我们不再需要它时,调用[foo release]
。
回答
让我们从"保留"和"释放"开始;一旦了解了基本概念,autorelease
实际上只是一个特例。
在Cocoa中,每个对象都会跟踪其被引用的次数(特别是NSObject基类实现了此操作)。通过在一个对象上调用retain
,就告诉它我们想将其引用计数加1. 通过调用release
,我们告诉对象我们正在放开它,并且它的引用计数递减。如果在调用release
之后,引用计数现在为零,则系统将释放该对象的内存。
与malloc
和free
不同的基本方法是,任何给定的对象都不必担心系统的其他部分崩溃,因为我们已经释放了它们正在使用的内存。假设每个人都按照规则进行游戏并保留/释放,则当保留一段代码然后释放该对象时,其他引用该对象的代码也不会受到影响。
有时可能会造成混淆的是,知道在什么情况下应该调用"保留"和"释放"。我的一般经验法则是,如果我想在某个对象上停留一段时间(例如,如果它是类中的成员变量),那么我需要确保该对象的引用计数了解我。如上所述,通过调用"保留"来增加对象的参考计数。按照约定,当使用" init"方法创建对象时,它也会增加(实际上设置为1)。在这两种情况下,当我处理完对象时,我都有责任在对象上调用release
。如果我不这样做,将会发生内存泄漏。
创建对象的示例:
NSString* s = [[NSString alloc] init]; // Ref count is 1 [s retain]; // Ref count is 2 - silly // to do this after init [s release]; // Ref count is back to 1 [s release]; // Ref count is 0, object is freed
现在用于autorelease
。自动释放是一种方便的(有时是必要的)方式,可以告诉系统一段时间后释放该对象。从管道角度来看,当调用autorelease
时,当前线程的NSAutoreleasePool
会收到有关该调用的警报。 NSAutoreleasePool现在知道一旦有机会(在事件循环的当前迭代之后),就可以在对象上调用release。从我们作为程序员的角度来看,它负责为我们调用release
,因此我们不必(事实上,我们不应该)。
需要注意的重要一点是(再次按照惯例)所有对象创建类方法都返回一个自动释放的对象。例如,在下面的示例中,变量" s"的引用计数为1,但是在事件循环完成后,它将被销毁。
NSString* s = [NSString stringWithString:@"Hello World"];
如果要挂在该字符串上,则需要显式调用" retain",然后在完成后显式"释放"它。
考虑下面的代码(非常人为的),我们将看到需要autorelease
的情况:
- (NSString*)createHelloWorldString { NSString* s = [[NSString alloc] initWithString:@"Hello World"]; // Now what? We want to return s, but we've upped its reference count. // The caller shouldn't be responsible for releasing it, since we're the // ones that created it. If we call release, however, the reference // count will hit zero and bad memory will be returned to the caller. // The answer is to call autorelease before returning the string. By // explicitly calling autorelease, we pass the responsibility for // releasing the string on to the thread's NSAutoreleasePool, which will // happen at some later time. The consequence is that the returned string // will still be valid for the caller of this function. return [s autorelease]; }
我知道所有这些在某些时候都会让人感到困惑,但是它会点击。以下是一些入门的参考:
- 苹果对内存管理的介绍。
- Aaron Hillegas撰写的Mac OS X可可编程(第四版)-写得很好,有很多很好的例子。它读起来像一个教程。
- 如果我们真的要潜入水中,则可以前往Big Nerd Ranch。这是由上述书籍的作者亚伦·希勒加斯(Aaron Hillegas)运营的培训机构。几年前,我参加了那里的可可入门课程,这是学习的好方法。
回答
Matt Dillard wrote: return [[s autorelease] release];
自动释放不会保留该对象。自动发布只是将其放入队列中,以便稍后发布。我们不想在那里有一个发布声明。
回答
如果要为台式机编写代码,并且可以将Mac OS X 10.5作为目标,则至少应考虑使用Objective-C垃圾收集。它确实可以简化大部分开发工作,这就是Apple首先将所有精力都投入到创建它并使其性能良好的原因。
至于不使用GC时的内存管理规则:
- 如果我们使用+ alloc / + allocWithZone:,+ new,-copy或者-mutableCopy创建新对象,或者保留对象,则必须拥有该对象的所有权,并且必须确保该对象的所有权发送
-release
。 - 如果我们以其他任何方式收到对象,则我们不是该对象的所有者,并且不应确保将其发送为" -release"。
- 如果我们想确保对象被发送了" -release",则可以自己发送,也可以发送对象" -autorelease",当前的自动释放池将其发送给" -release"(每收到一次" -autorelease")。 `)在游泳池排干水时。
通常,"-autorelease"被用作一种确保对象在当前事件持续时间内生存的方式,但由于在Cocoa的事件处理过程中存在一个自动释放池,因此在此之后对其进行了清理。在Cocoa中,将对象返回给自动释放的调用者比返回调用者本身需要释放的objets更为普遍。
回答
如果我们了解保留/释放的过程,那么对于成熟的Cocoa程序员而言,有两个黄金法则是显而易见的,但是不幸的是,对于新手来说,很少能清楚地说明这一点。
- 如果返回对象的函数名称中包含" alloc"," create"或者" copy",则该对象就是。完成后必须调用
[object release]
。或者CFRelease(object)
,如果它是一个Core-Foundation对象。 - 如果名称中没有这些单词之一,则该对象属于其他人。如果我们希望在函数结束后保留对象,则必须调用" [对象保留]"。
我们在自己创建的函数中也遵循此约定将非常有用。
(Nitpickers:是的,不幸的是,有一些API调用是这些规则的例外,但很少见)。
回答
也有很多关于cocoadev的好信息:
- 内存管理
- 经验法则
回答
与以往一样,当人们开始尝试重新撰写参考资料时,他们几乎总是会出错或者提供不完整的描述。
Apple在《用于Cocoa的内存管理编程指南》中提供了对Cocoa内存管理系统的完整描述,在其末尾有关于内存管理规则的简短但准确的摘要。
回答
上面的答案清楚地重述了文档中所说的内容;大多数新人遇到的问题是无证案件。例如:
- 自动发布:文档称它将在"将来某个时候"触发发布。什么时候?!基本上,我们可以依靠周围的对象,直到将代码退出回到系统事件循环为止。系统可以在当前事件周期之后的任何时间释放对象。 (我想马特早先说过。)
- 静态字符串:
NSString * foo = @" bar";
-我们必须保留还是释放它?不,怎么样
-(void)getBar { return @"bar"; }
...
NSString *foo = [self getBar]; // still no need to retain or release
- 创建规则:如果创建了该规则,则说明我们拥有它,并希望将其发布。
通常,新的Cocoa程序员陷入困境的方式是不了解哪个例程会返回一个带有" retainCount> 0"的对象。
这是可可存储器管理的非常简单规则的摘录:
Retention Count rules Within a given block, the use of -copy, -alloc and -retain should equal the use of -release and -autorelease. Objects created using convenience constructors (e.g. NSString's stringWithString) are considered autoreleased. Implement a -dealloc method to release the instancevariables you own
第一个项目符号说:如果调用了alloc(或者new fooCopy),则需要在该对象上调用release。
第二个项目符号说:如果使用便利构造函数,并且需要将该对象挂起(与稍后绘制的图像一样),则需要保留(然后发布)。
第三个应该是不言自明的。
回答
iDeveloperTV网络提供了免费的截屏视频
Objective-C中的内存管理
回答
除了我们可能想考虑降低$ 50并获取Hillegass书籍外,我不会添加其他保留/发布的内容,但我强烈建议我们在应用程序开发的早期就开始使用Instruments工具(即使我们第一!)。为此,请运行->从性能工具开始。我将从泄漏工具开始,它只是可用的许多工具中的一种,但是当我们忘记发布时,它将有助于向我们显示。令人生畏的是,我们将获得多少信息,这实在令人生畏。但是请查看本教程以快速起步:
可可补习:用仪器修复记忆渗漏
实际上,尝试强制泄漏可能是学习如何预防泄漏的更好方法!祝你好运 ;)
回答
我通常收集的Cocoa内存管理文章:
可可记忆管理
回答
正如一些人已经提到的那样,Apple的"内存管理简介"是迄今为止最好的起点。
我还没有提到的一个有用的链接是实用内存管理。如果我们通读它们,将会在Apple文档的中间找到它,但是值得直接链接。这是有关内存管理规则的精彩摘要,其中包含示例和常见错误(基本上是这里试图解释的其他答案,但不是很好)。