使用Cocoa和Objective-C了解引用计数

Mat*_*ard 122 memory iphone cocoa objective-c

我刚开始看看Objective-C和Cocoa,以便玩iPhone SDK.我对C mallocfree概念感到相当舒服,但Cocoa的引用计数方案让我很困惑.一旦你理解了它,我就被告知它非常优雅,但我还没有超过驼峰.

怎么办release,retainautorelease工作有什么关于他们使用的约定?

(或者失败了,你读了什么帮助你得到它?)

Mat*_*ard 148

让我们从retain和开始release; autorelease一旦你理解了基本概念,这真的只是一个特例.

在Cocoa中,每个对象都会跟踪它被引用的次数(具体来说,NSObject基类实现了这一点).通过调用retain一个对象,您告诉它您希望将其引用计数加1.通过调用release,您告诉对象您放弃它,并且它的引用计数递减.如果在调用之后release引用计数现在为零,则系统将释放该对象的内存.

这与它的基本方式不同,malloc并且free任何给定的对象都不需要担心系统崩溃的其他部分,因为你已经释放了他们正在使用的内存.假设每个人都在按照规则进行游戏并保留/释放,当一段代码保留然后释放该对象时,任何其他引用该对象的代码都不会受到影响.

什么有时会混淆是知道下,你应该调用情况retainrelease.我的一般经验法则是,如果我想在一个对象上持续一段时间(例如,如果它是类中的成员变量),那么我需要确保对象的引用计数知道我.如上所述,通过调用增加对象的引用计数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
Run Code Online (Sandbox Code Playgroud)

现在为autorelease.Autorelease用作方便(有时是必要的)方式告诉系统在一段时间后释放此对象.从管道的角度来看,当autorelease被调用时,当前线程会NSAutoreleasePool被警告该呼叫.在NSAutoreleasePool现在知道,一旦它得到一个机会(事件循环的当前迭代之后),它可以调用release的对象.从我们作为程序员的角度来看,它需要照顾release我们,所以我们没有(事实上,我们不应该).

需要注意的是,(按照惯例),所有对象创建方法都返回一个自动释放的对象.例如,在以下示例中,变量"s"的引用计数为1,但在事件循环完成后,它将被销毁.

NSString* s = [NSString stringWithString:@"Hello World"];
Run Code Online (Sandbox Code Playgroud)

如果要挂起该字符串,则需要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];
}
Run Code Online (Sandbox Code Playgroud)

我意识到所有这一切都有点令人困惑 - 但在某些时候,它会点击.以下是一些可以帮助您前进的参考资料:

  • Apple对内存管理的介绍.
  • "可可编程为Mac OS X(第4版)",作者:Aaron Hillegas - 一本写得很好的书,里面有很多很好的例子.它读起来就像一个教程.
  • 如果你真的潜水,你可以前往Big Nerd Ranch.这是由Aaron Hillegas经营的培训机构 - 上述书籍的作者.几年前我参加了可可课程简介,这是一个很好的学习方式.

  • 你写道:"通过调用autorelease,我们暂时撞击引用计数".我认为这是错的; autorelease仅标记将来要发布的对象,它不会增加引用计数:http://cocoadev.com/index.pl?AutoRelease (8认同)
  • 有趣的琐事:由于答案使用@""和NSString,因此字符串在整个过程中是不变的,因此,绝对保留计数将是常量且完全不相关....不会以任何方式使答案错误,只是强调绝对保留计数从来不是你应该担心的事实. (6认同)
  • 非常感谢您的好解释.只有一件事还不清楚.如果`NSString*s = [[NSString alloc] initWithString:@"Hello World"];`返回一个自动释放的对象(当你编写它时)为什么我要做一个`return [s autorelease];`并设置它" autorelease"再次,而不仅仅是'返回s`? (3认同)
  • @Stefan:`[[NSString alloc] initWithString:@"Hello World"]`不会返回自动释放的对象.每当调用`alloc`时,引用计数都设置为1,并且该代码负责确保它被释放.另一方面,`[NSString stringWithString:]`调用_does_返回一个自动释放的对象. (3认同)
  • "现在进行自动释放.自动释放被用作一种方便的(有时是必要的)方式来告诉系统在一段时间后释放这个物体." 作为引导句,这是错误的.它不告诉系统"释放它",它告诉它减少保留计数. (2认同)

And*_*ant 10

如果您了解保留/释放的过程,那么对于已建立的Cocoa程序员来说,有两个很明显的"黄色"规则,但不幸的是,对于新手来说很少明白这一点.

  1. 如果返回一个对象的函数有alloc,create或者copy在其名称中,那么该对象就是你的.完成后你必须打电话[object release].或者CFRelease(object),如果它是Core-Foundation对象.

  2. 如果它的名称中没有这些单词之一,那么该对象属于其他人.[object retain]如果您希望在功能结束后保留​​对象,则必须调用.

您可以在自己创建的函数中遵循此约定.

(Nitpickers:是的,不幸的是有一些API调用是这些规则的例外,但它们很少见).

  • 这是不完整和不准确的.我仍然无法理解为什么人们试图重复规则而不是简单地指向相关文档:http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Tasks/MemoryManagementRules.html (11认同)
  • 特别是核心基金会的规则与Cocoa的规则不同; 请参阅http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html (4认同)
  • 对不起!我认为我仓促地投了反对票。[内存管理规则](http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH)你的答案差不多引用苹果文档。 (2认同)

Chr*_*son 8

如果您正在为桌面编写代码并且可以定位Mac OS X 10.5,那么至少应该考虑使用Objective-C垃圾回收.它真的会简化你的大部分开发 - 这就是为什么Apple会把所有的努力放在首先创建它,并使它表现良好.

至于不使用GC时的内存管理规则:

  • 如果使用+alloc/+allocWithZone:,或+new,-copy或者对象创建新对象,-mutableCopy则表示您-retain拥有该对象,并且必须确保将其发送-release.
  • 如果您收到任何其他方式的对象,你是不是它的主人,应该保证其发送-release.
  • 如果要确保发送对象,-release您可以自己发送,也可以发送对象-autorelease,当池耗尽时,当前自动释放池将发送它-release(每次收到一次-autorelease).

通常-autorelease用作确保对象在当前事件的长度内生存的方式,但之后会被清理,因为有一个自动释放池围绕Cocoa的事件处理.在可可,它是远远更常见的对象返回到被自动释放比它返回调用者本身需要释放OBJETS呼叫者.


Nil*_*ect 6

Objective-C使用引用计数,这意味着每个Object都有一个引用计数.创建对象时,它的引用计数为"1".简单地说,当一个对象被引用(即存储在某个地方)时,它被"保留",这意味着它的引用计数增加了一个.当不再需要一个对象时,它被"释放",这意味着它的引用计数减少了一个.

当对象的引用计数为0时,将释放该对象.这是基本的参考计数.

对于某些语言,引用会自动增加和减少,但objective-c不是这些语言之一.因此程序员负责保留和释放.

编写方法的典型方法是:

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;
Run Code Online (Sandbox Code Playgroud)

需要记住在代码中释放任何获取的资源的问题既繁琐又容易出错.Objective-C引入了另一个旨在使这更容易的概念:Autorelease Pools.自动释放池是安装在每个线程上的特殊对象.如果你查找NSAutoreleasePool,它们是一个相当简单的类.

当一个对象发送一个"autorelease"消息时,该对象将查找当前线程的堆栈上的任何自动释放池.它会将对象添加到列表中作为对象,以便在将来的某个时刻发送"释放"消息,这通常是在池本身被释放时.

使用上面的代码,您可以通过以下方式将其重写为更短且更易于阅读:

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;
Run Code Online (Sandbox Code Playgroud)

因为该对象是自动释放的,所以我们不再需要在其上显式调用"release".这是因为我们知道一些自动释放池将在以后为我们做.

希望这会有所帮助.维基百科的文章非常适合引用计数.有关自动释放池的更多信息,请访问此处.另请注意,如果您正在为Mac OS X 10.5及更高版本构建,则可以告诉Xcode在启用垃圾收集的情况下进行构建,从而允许您完全忽略retain/release/autorelease.

  • 这是错的.在所示的任一示例中都不需要发送someObject版本或自动发布. (2认同)

Mat*_*ard 6

约书亚(#6591) - Mac OS X 10.5中的垃圾收集看起来很酷,但不适用于iPhone(或者如果你希望你的应用程序在10.5之前版本的Mac OS X上运行).

此外,如果您正在编写库或可能重用的内容,使用GC模式会将使用该代码的任何人锁定到也使用GC模式,因此据我了解,任何试图编写广泛可重用代码的人都倾向于管理手动记忆.

  • 编写支持GC和引用计数的混合框架是完全可能的. (2认同)

mma*_*alc 6

与以往一样,当人们开始尝试重新编写参考资料时,他们几乎总是会出错或提供不完整的描述.

Apple在Cocoa的内存管理编程指南中提供了Cocoa内存管理系统的完整描述,最后有一个简短而准确的内存管理规则摘要.

  • 实际上,这是一个更好的单页摘要:http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html (2认同)

Rob*_*Rob 6

我不会添加保留/释放的具体内容,除了你可能想要降低50美元并获得Hillegass书籍,但我强烈建议在应用程序开发的早期阶段开始使用Instruments工具(甚至是你的第一!).为此,请运行 - >使用性能工具.我从泄漏开始,这只是许多可用仪器中的一种,但是当你忘记发布时会有助于告诉你.这是令人畏惧的,你将获得多少信息.但请查看本教程以快速起步:
COCOA TUTORIAL:使用仪器修复内存泄漏

实际上,试图强制泄漏可能是一种更好的方法,反过来,学习如何防止它们!祝好运 ;)


Nil*_*ect 5

Matt Dillard写道:

return [[s autorelease] release];

自动释放并没有挽留的对象.自动释放只是将其放入队列中以便稍后释放.你不想在那里有发布声明.


小智 5

我通常收集的Cocoa内存管理文章:

可可记忆管理