【问题标题】:Ownage of objects you create您创建的对象的所有权
【发布时间】:2015-05-28 11:50:18
【问题描述】:

我知道讨论过很多次,但我在研究内存管理时有一点困惑。

它说,您拥有对象的所有权,您可以使用 alloc/copy/new/mutableCopy 或类似的方法(如 allocWithSomething)创建。

但是,当你在ARC中创建NSArray *arr = [NSArray array];这样的对象时,就相当于写NSArray *arr = [[NSArray array]autorelease];

但是,我经常看到这样的说法——你拥有你创建的每一个对象。但在上面的例子中,我们实际上并不拥有对象,我们只是将它添加到autorelease pool。因此,我们不必保留/释放它,它会在它离开函数范围后立即消失(正如苹果所说,在“事件”之后)。

您能否为我澄清一下,我们真的拥有我们创建的每个对象,还是只拥有使用 alloc/copy/mutableCopy/new 创建的对象,因此将它们的保留计数增加了 +1?

【问题讨论】:

    标签: ios objective-c memory-management


    【解决方案1】:

    这是 Objective-C 中所有权术语的问题。最简单的规则是,如果在我们使用完对象后必须释放该对象,我们就拥有该对象。因此,所有者可以是一个类,可以是一个方法或一个函数,甚至是一个块,而且还有更多。同一个对象可能有多个所有者。

    可以通过release 方法或autorelease 方法调用该版本。我们将它添加到自动释放池中以延迟它的释放,直到池被耗尽,这在大多数情况下是在运行循环结束时。您发布的代码与[[NSArray array]autorelease]; 有误,因为这会使您的应用程序由于过度释放而崩溃。正确的版本是[[NSArray new] autorelease];,这或多或少是静态方法array 返回的内容。

    因此,如果我们暂时排除 autorelease,我们有 3 个元素:retain 将获得对象的所有权并增加保留计数,同时迫使您在某个时候释放它; release 将放弃所有权,减少保留计数,如果保留计数降至零(调用者是最后一个所有者),则可能释放对象;对象创建规则表明所有包含 create、alloc、new、copy... 的方法或函数将授予您所有权而不保留它们,并且您有责任显式调用它们的 release。

    创建对象时,它的保留计数为 1。这意味着我们调用 alloc+init 的每个对象或其任何版本的保留计数都将为 1,并且需要显式释放。 [NSArray array] 等所有其他静态方法对[[[NSArray alloc] init] autorelease] 来说只是方便,需要保留这些方法才能接管所有权。

    所以基本上如果你有一个类有一个数组指针NSArray *_array,并且我们想获得所有权,我们必须写_array = [someArray retain],在dealloc方法中我们必须写[_array release]。如果一个方法需要取得所有权,我们需要调用[someArray retain],当我们完成它时,我们需要调用[someArray release],除非我们返回对象本身,我们需要调用return [someArray autorelease]。这些是非常简单的规则,但在其他一些情况下,很难跟踪。因此,ARC 所做的就是将这些调用插入到您的代码中,您无需担心它们。与 ARC 之前一样,这些程序仍然存在。

    所以 ARC 是如何插入这些调用的并没有真正解释,并且与版本不同。但是,如果我们再看一下您发布的示例,但让我们对其进行扩展:

    - (NSArray *)foo // ARC version
    {
        NSArray *arr = [NSArray array];
        for(NSObject *object in arr) NSLog(@"%@", object.description);
        arr = [arr arrayByAddingObject:@"added something"];
        return arr;
    }
    
    - (NSArray *)foo // ARC most likely behind the scenes
    {
        NSArray *arr = [[NSArray array] retain]; // reatin to claim ownership
        for(NSObject *object in arr) NSLog(@"%@", object.description);
        [arr autorelease]; // need to release it as it will be lost in the next line
        arr = [[arr arrayByAddingObject:@"added something"] retain]; // the arr pointer now points to a different object. Claim the ownership of it
        return [arr autorelease]; // need to relinquish the ownership
    }
    
    - (NSArray *)foo // standart non ARC wersion
    {
        NSArray *arr = [[NSArray alloc] init]; // we have the ownership by default
        for(NSObject *object in arr) NSLog(@"%@", object.description);
        NSArray *newArray = [[arr arrayByAddingObject:@"added something"] retain]; // need to explicitly retain this one as it is on the autorelease pool
        [arr release]; // We do not need it anymore
        return [newArray autorelease]; // need to relinquish the ownership
    }
    

    【讨论】:

    • 谢谢你,答案很清楚,现在我可以看到全貌了 :) 不过,为什么许多作者教你,你创造的任何对象都是你自己的?根据您上面的描述,我可以说 [NSArray 数组];不是我们拥有并对其负责的对象,而是我们可能不关心的自动释放对象(如果我理解正确的池将在每个函数之后耗尽)。因此,我们只对 alloc/copy/new/..? 的对象拥有/负责?
    • 是的,评估者对收到的任何关键词以及其他关键词以及我们保留的关键词负责。这很重要,因为它们根本不一定是创建的。每次功能后,游泳池都不会流失,远非如此。它肯定会在运行循环之后,但这对于这个问题来说太复杂了。如果您有兴趣,请尝试做一些研究......
    • 好吧,那么池什么时候会耗尽?正如苹果所说,在“事件”之后。
    • 想象一下,您有一个无限循环和一个包含执行所需调用的数组。每次使用 performSelector 时都会创建一个调用并将其添加到此数组中,每个计时器都会在此数组上添加一个调用...现在在循环的每次迭代中,所有触摸事件或其他外部按钮事件都将被收集并发送到主窗口然后向下到可见的视图控制器,它们的视图......然后将调用其所有重写的方法,并在所有数组调用之后(或之前)调用它们,然后耗尽所有释放池。这接近于 runloop 所做的......
    • 在那个解释中,您抽象地定义了一个应用程序生命周期?如果我理解正确,您尝试解释一下,有一个抽象数组收集“事件”和要发送的方法,并且在它完成启动所有事件/方法(应用程序结束)后它会耗尽池?也许我错了,我想几句话很难解释清楚,但我感谢你的帮助。
    【解决方案2】:

    这可能过于简单了,但是当你调用 [NSArray 数组] 时,你实际上并没有请求内存空间,NSArray 类正在为你做这件事。 NSArray 类有一个可能看起来像这样的方法:

      + (NSArray *) array
      {
          return [[[NSArray alloc] init] autorelease];
      }
    

    所以它创建对象并“拥有”它,而不是你。

    对于它的价值,而不是“拥有”,我更愿意将其视为“负责”。如果您使用 alloc 和类似方法为对象请求内存,那么您有责任释放该内存。

    【讨论】:

    • 谢谢 Mike 的好回答!
    【解决方案3】:

    您拥有您创建的每一个对象 - 只要您希望拥有它。 “拥有”意味着您持有对象的保留计数,并且您将释放该保留计数,或者将责任和所有权转移给其他人。

    [[NSArray alloc] init...]
    

    创建一个您当时拥有的对象,保留计数为 1。您可以将该对象传递给接管所有权的其他人。

    [NSArray array]
    

    创建并返回一个对象,但立即将其添加到自动释放池中。该对象在返回时不归任何人所有;自动释放池只是防止它被释放,直到自动释放池本身消失。任何愿意的人都可以拥有该对象的所有权。当然,不止一位代码可以获取对象的所有权。当每个人释放所有权时,该对象将消失,并且当自动释放池也消失时。

    无论如何,ARC 会为您处理所有这些事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-07
      • 2019-01-17
      • 1970-01-01
      • 2019-06-27
      • 1970-01-01
      相关资源
      最近更新 更多