Answers:
让我们从retain
和开始release
。autorelease
一旦了解了基本概念,这实际上只是一个特例。
在Cocoa中,每个对象都跟踪它被引用了多少次(具体来说,NSObject
基类实现了此目的)。通过调用retain
一个对象,您就是在告诉该对象要将其引用计数加一。通过调用release
,您告诉您要释放的对象,并且其引用计数递减。如果在调用之后release
,引用计数现在为零,则系统将释放该对象的内存。
基本方式这不同于malloc
并free
为任何给定的对象并不需要对系统崩溃,因为你他们使用释放的内存的其他部分忧虑。假设每个人都按照规则进行游戏并保留/释放,则当保留一段代码然后释放该对象时,任何其他引用该对象的代码也不会受到影响。
有时可能造成混淆的是,您知道应该在什么情况下致电retain
和release
。我的一般经验法则是,如果我想在某个对象上停留一段时间(例如,如果它是类中的成员变量),那么我需要确保该对象的引用计数了解我。如上所述,对象的引用计数通过调用来增加retain
。按照惯例,当使用“ 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
显式调用,然后release
在完成后显式调用。
考虑下面的代码(非常人为的),您将看到一种情况: 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];
}
我意识到所有这些都有些令人困惑-尽管在某些时候它会点击。以下是一些帮助您入门的参考:
NSString* s = [[NSString alloc] initWithString:@"Hello World"];
返回一个自动释放的对象(如您所写),为什么我必须做一个return [s autorelease];
并再次将其设置为“自动释放”,而不仅仅是return s
?
[[NSString alloc] initWithString:@"Hello World"]
不会返回自动释放的对象。每当alloc
调用时,引用计数都设置为1,该代码负责确保将其释放。[NSString stringWithString:]
另一方面,该调用的确会返回一个自动释放的对象。
如果您了解保留/释放的过程,那么对于已建立的Cocoa程序员来说,有两个黄金法则是“ du”的,但是不幸的是,对于新手来说,很少能清楚地阐明这一点。
如果它返回一个对象的函数具有alloc
,create
或copy
在其名称则该对象是你的。[object release]
完成后必须致电。或CFRelease(object)
,如果它是一个Core-Foundation对象。
如果名称中没有这些单词之一,则该对象属于其他人。[object retain]
如果希望在函数结束后保留该对象,则必须调用。
您在自己创建的函数中也遵循此约定将非常有用。
(Nitpickers:是的,不幸的是,有一些API调用是这些规则的例外,但很少见)。
如果要为台式机编写代码,并且可以将Mac OS X 10.5作为目标,则至少应考虑使用Objective-C垃圾收集。它确实可以简化您的大部分开发工作-这就是Apple首先将所有精力都投入到创建它并使其性能良好的原因。
至于不使用GC时的内存管理规则:
+alloc/+allocWithZone:
,或创建新对象+new
,-copy
或者-mutableCopy
您-retain
拥有一个对象,那么您将拥有该对象的所有权并且必须确保已发送该对象-release
。-release
。-release
可以自己发送,也可以发送对象-autorelease
,并且当前自动释放池将在耗尽该池时发送该对象-release
(每次接收一次-autorelease
)。通常,这-autorelease
是一种用于确保对象在当前事件持续时间内生存的方式,但随后会被清除,因为围绕Cocoa的事件处理有一个自动释放池。在可可,它是远远更常见的对象返回到被自动释放比它返回OBJETS调用者本身需要释放调用者。
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在启用垃圾回收的情况下进行构建,从而使您可以完全忽略保留/释放/自动释放。
与以往一样,当人们开始尝试重新撰写参考资料时,他们几乎总是会出错或提供不完整的描述。
Apple在《用于Cocoa的内存管理编程指南》中提供了对Cocoa内存管理系统的完整描述,在其末尾有关于内存管理规则的简短但准确的摘要。
除了您可能想考虑降低$ 50并获取Hillegass书籍外,我不会添加其他保留/发布的内容,但我强烈建议您在应用程序开发的早期就开始使用Instruments工具(即使您第一!)。为此,请运行->从性能工具开始。我将从泄漏工具开始,它只是众多可用工具中的一种,但是当您忘记发布时,它将有助于向您显示。令人生畏的是,您将获得多少信息,这实在令人生畏。但是请查看本教程以快速
起步:可可教程:用仪器修复内存泄漏
实际上,尝试强制泄漏可能是学习如何预防泄漏的更好方法!祝好运 ;)
返回[[s autorelease]发布];
自动释放并没有挽留的对象。自动发布只是将其放入队列中,以便稍后发布。您不想在那里有发布声明。
NilObject的答案是一个好的开始。以下是与手动内存管理有关的一些补充信息(iPhone上要求)。
如果您个人alloc/init
是对象,则其引用计数为1。如果不再需要该对象,您有责任通过调用[foo release]
或清理该对象。[foo autorelease]
。release会立即清除它,而autorelease会将对象添加到autorelease池中,后者会在以后自动释放它。
autorelease主要用于当您有一个需要返回有问题的对象的方法时(因此您不能手动释放它,否则将返回nil对象),但又不想保留它。
如果获取的对象没有调用alloc / init来获取它-例如:
foo = [NSString stringWithString:@"hello"];
但您想挂接到该对象,则需要调用[foo keep]。否则,很可能会得到,autoreleased
并且您将坚持使用nil引用(如上stringWithString
例所示)。当您不再需要它时,请致电[foo release]
。
上面的答案清楚地重述了文档中所说的内容;大多数新人遇到的问题是无证案件。例如:
自动发布:文档称它将“在将来的某个时候”触发发布。什么时候?!基本上,您可以依靠周围的对象,直到将代码退出回到系统事件循环为止。系统可以在当前事件周期之后的任何时间释放对象。(我想,马特早先说过。)
静态字符串:NSString *foo = @"bar";
-您必须保留还是释放它?不,怎么样
-(void)getBar {
return @"bar";
}
...
NSString *foo = [self getBar]; // still no need to retain or release
创建规则:如果创建了该规则,则说明您拥有它,并希望将其发布。
通常,新的Cocoa程序员陷入困境的方式是不了解哪个例程返回带有对象的对象。 retainCount > 0
。
这是可可内存管理的非常简单规则的摘录:
保留计数规则
- 在给定的块中,使用-copy,-alloc和-retain应该等于使用-release和-autorelease。
- 使用便利构造函数创建的对象(例如,NSString的stringWithString)被认为是自动释放的。
- 实现-dealloc方法以释放您拥有的实例变量
第一个项目符号说:如果调用了alloc
(或new fooCopy
),则需要在该对象上调用release。
第二个项目符号说:如果使用便利构造函数,并且需要将该对象挂起(就像稍后绘制的图像一样),则需要保留(然后发布)。
第三个应该是不言自明的。