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

wodio 发布于 2018-03-09 cocoa 最后更新 2018-03-09 01:07 663 浏览

我只是开始看看Objective-C和Cocoa,以便使用iPhone SDK。我对C的mallocfree概念很合适,但Cocoa的参考计数方案让我颇为困惑。一旦你了解它,我就会被告知它非常优雅,但我还没有完成。 releaseretainautorelease如何工作以及关于它们使用的约定是什么? (或者说失败了,你读了哪些帮助你得到的东西?)

已邀请:

oqui

赞同来自:

Objective-C使用Reference Counting,这意味着每个对象都有一个引用计数。当一个对象被创建时,它的引用计数为“1”。简单地说,当一个对象被引用(即存储在某处)时,它会被“保留”,这意味着它的引用计数增加1。当一个对象不再需要时,它被“释放”,这意味着它的引用计数减1。 当一个对象的引用计数为0时,该对象被释放。这是基本的参考计数。 对于某些语言,引用会自动增加和减少,但objective-c不是这些语言之一。因此程序员负责保留和释放。 编写一种方法的典型方法是:

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;
需要记住在代码中释放任何获得的资源的问题既乏味又容易出错。 Objective-C引入了另一个旨在简化它的概念:Autorelease Pools。自动释放池是安装在每个线程上的特殊对象。如果你查找NSAutoreleasePool,它们是一个相当简单的类。 当一个对象获得发送给它的“autorelease”消息时,该对象将查找位于当前线程堆栈上的任何autorelease池。它会将对象添加到列表中,作为将来发送“释放”消息的对象,这通常是在池本身被释放时。 考虑到上面的代码,你可以重写它以缩短和更容易阅读:
id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;
由于对象是自动释放的,因此我们不再需要在其上显式调用“释放”。这是因为我们知道一些autorelease池会在稍后为我们做。 希望这有助于。维基百科的文章对引用计数非常有用。有关autorelease pools can be found here的更多信息。另请注意,如果您正在为Mac OS X 10.5及更高版本构建,则可以告诉Xcode使用垃圾回收进行构建,从而完全忽略保留/释放/自动释放。

wodio

赞同来自:

Joshua(#6591) - Mac OS X 10.5中的垃圾收集工具看起来很酷,但不适用于iPhone(或者如果您希望您的应用程序在10.5以前版本的Mac OS X上运行)。 另外,如果你正在编写一个库或者可能被重用的东西,那么使用GC模式将任何使用代码的人锁定到同样使用GC模式,据我所知,任何试图编写广泛可重用代码的人都倾向于管理手动记忆。

ut_eos

赞同来自:

NilObject的答案是一个好的开始。以下是有关手动内存管理的补充信息( iPhone需要)。 如果您是alloc/init对象,则它的引用计数为1.您需要在不再需要时清理它,或者调用[foo release][foo autorelease]。 release会立即清除它,而autorelease会将该对象添加到autorelease池中,该池会在稍后自动释放它。 autorelease主要用于当你有一个方法需要返回有问题的对象(所以你不能手动释放它,否则你会返回一个零对象),但你不想也要坚持下去。 如果你获得了一个你没有调用alloc/init来获得它的对象 - 例如:

foo = [NSString stringWithString:@"hello"];
但你想挂在这个对象上,你需要调用[foo retain]。否则,有可能会获得autoreleased,并且您将继续参考(如上面的stringWithString示例中所示)。当您不再需要它时,请致电[foo release]

dab

赞同来自:

我们先从retainrelease开始;一旦你理解了基本概念,autorelease实际上只是一个特例。 在Cocoa中,每个对象会跟踪它被引用的次数(具体来说,NSObject基类实现了这个功能)。通过在对象上调用retain,您告诉它您想要将其引用计数加1。通过调用release,您可以告诉您放弃的对象,并且其引用计数会减少。如果在调用release之后,引用计数现在为零,则系统释放该对象的内存。 这与mallocfree不同的基本方式是,任何给定的对象都不需要担心系统崩溃的其他部分,因为您释放了他们正在使用的内存。假设每个人都按照规则玩耍并保留/释放,当一段代码保留然后释放该对象时,任何其他代码引用的对象也不会受到影响。 有时可能会引起混淆的是知道您应该在哪些情况下调用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
现在autorelease。 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];
}
我意识到这一切都有点令人困惑 - 但在某些时候,它会点击。这里有几个引用来帮助你:
  • Apple's introduction到内存管理。
  • Cocoa Programming for Mac OS X (4th Edition),由Aaron Hillegas编写 - 写得很好,书中有很多很棒的例子。它看起来像一个教程。
  • 如果您真的潜入,您可以前往Big Nerd Ranch。这是一个由Aaron Hillegas负责的培训机构 - 这是上述书的作者。几年前,我参加了可可课程介绍,这是一个很好的学习方式。

oqui

赞同来自:

Matt Dillard wrote: return [[s autorelease] release];
自动释放保留该对象。 Autorelease只是放在队列中以便稍后发布。你不想在那里发布发布声明。

pquo

赞同来自:

如果您正在为桌面编写代码,并且您可以定位Mac OS X 10.5,则至少应该使用Objective-C垃圾收集进行研究。这真的会简化大部分的开发工作 - 这就是为什么苹果将所有的努力放在首位创造它,并使其表现良好。 至于不使用GC时的内存管理规则:

  • 如果您创建使用+alloc/+allocWithZone:+new-copy-mutableCopy或者如果你-retain一个对象,你正在服用它的所有权,必须确保它发送-release一个新的对象。
  • 如果以任何其它方式接收的对象,则是的它的主人,并应的确保它被发送-release
  • 如果你想确保一个对象发送-release您可以发送自己,或者你可以(每收到-autorelease一次)发送的对象-autorelease和当前的自动释放池将发送-release当池被排空。
  • 通常-autorelease被用作确保对象存在当前事件的长度的方式,但事后清理,因为是围绕可可的事件处理自动释放池。在Cocoa中,更常见的是将对象返回给自动发布的调用者,而不是返回调用者本身需要发布的objets。

et_est

赞同来自:

如果你理解保留/释放的过程,那么有两条黄金规则对于已建立的可可程序员来说是“明显的”,但不幸的是很少有人明确地为新手阐明了这一点。

  1. 如果返回对象的函数名称中包含alloccreatecopy,则该对象是您的。完成后您必须调用[object release]。或CFRelease(object),如果它是Core-Foundation对象。
  2. 如果它的名字中没有这些单词中的任何一个,则该对象属于其他人。如果您希望在函数结束后保留​​该对象,则必须调用[object retain]
你会很好地遵守这个惯例,你自己创造的功能。 (Nitpickers:是的,不幸的是有几个API调用是这些规则的例外,但它们很少见)。

baut

赞同来自:

很多有关cocoadev的良好信息:

qodio

赞同来自:

与往常一样,当人们开始尝试重新编写参考资料时,他们几乎总是出错或提供不完整的描述。 Apple在Memory Management Programming Guide for Cocoa中提供了Cocoa内存管理系统的完整描述,最后简要但精确地总结了Memory Management Rules

ket

赞同来自:

上述答案明确说明了文件中的内容;大多数新人遇到的问题是无证件的情况。例如:

  • 自动发布:文档称它将在未来的某个时刻触发发布。什么时候?!基本上,你可以指望对象在你的代码退回到系统事件循环中。系统可以在当前事件循环后的任何时候释放对象。 (我认为马特早些时候说过。)
  • 静态字符串NSString *foo = @"bar"; - 您是否必须保留或释放该字符串?不,那怎么样
    -(void)getBar {
        return @"bar";
    }
    
    ...
    NSString *foo = [self getBar]; // still no need to retain or release
    
  • 创建规则:如果您创建了它,则您拥有它,并且预计将其发布。
  • 一般来说,新Cocoa程序员弄乱的方式是不理解哪些例程用retainCount > 0返回对象。 以下是来自Very Simple Rules For Memory Management In Cocoa的片段:
    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),则需要调用该对象的发布。 第二个项目符号表示:如果使用便利构造函数,并且需要将对象留在(如稍后将绘制的图像一样),则需要保留(然后再释放)它。 第三个应该是不言自明的。

nodio

赞同来自:

iDeveloperTV Network提供免费的屏幕录像 Memory Management in Objective-C

mest

赞同来自:

我不会添加保留/发布的具体内容,除非您想要考虑降低50美元并购买Hillegass书籍,但我强烈建议在应用程序开发的早期就开始使用Instruments工具(即使您的应用程序第一!)。为此,请运行 - >从性能工具开始。我会从Leaks开始,它只是许多可用工具中的一个,但会在您忘记发布时帮助向您展示。它会放弃你将要提交的多少信息。但看看这个教程快速起床:
COCOA TUTORIAL: FIXING MEMORY LEAKS WITH INSTRUMENTS 实际上,尝试强制泄漏可能是更好的方法,反过来,学习如何预防它们!祝你好运 ;)

funde

赞同来自:

我通常收集的可可内存管理文章: cocoa memory management

dodit

赞同来自:

正如几位人士所提到的,苹果的Intro to Memory Management是迄今为止最好的开始。 我还没有提到的一个有用的链接是Practical Memory Management。如果你仔细阅读,你会在苹果的文档中找到它,但它值得直接链接。这是对内存管理规则的一个辉煌的执行摘要,它带有例子和常见的错误(基本上还有其他答案试图解释什么,但不是那么好)。