是 - [NSArray lastObject]记录了返回自动释放的对象?

时间:2012-12-31 17:51:15

标签: objective-c cocoa-touch cocoa memory-management

我最近就lastObject是否返回自动释放的对象进行了this辩论。我一直认为它确实存在,而且我从来没有遇到过问题,但是,来考虑一下,大多数时候我使用这种方法,我的收藏品都是参考,所以,显然,即使它只是一个简单的参考,代码将工作正常。因此,关于返回值的自动释放性质,我似乎错了(基于一些SO答案),但是,我试图在Apple的文档中找到一些关于它的内容,但未能这样做。

Apple是否记录lastObject是否自动释放它返回的对象?如果是这样,它在哪里记录?如果事实证明这是真的,他们为什么这样做呢?我在上述辩论中得到的答案对我来说没有意义,该集合可以保留并自动释放该对象吗?这样,对象在runloop结束时仍然有效。

5 个答案:

答案 0 :(得分:7)

即使没有明确记录,lastObject可能与objectAtIndex:具有相同的行为。 objectAtIndex: retainautorelease根据此文档返回的对象:Avoid Causing Deallocation of Objects You’re Using

  

当一个对象从其中一个基本集合类中删除时,会向其发送一个释放(而不是自动释放)消息。如果集合是已删除对象的唯一所有者,则会立即释放已删除的对象(示例中为heisenObject)。

(强调我的)

请在同一文档页面中考虑此示例:

heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.

就内存管理而言,释放整个数组与删除所有对象是一样的,所以你不能指望返回的对象在runloop结束之前保持有效,即使它实际上可能没有在某些情况下解除分配。环境(例如文字字符串,单例,对象的其他引用等)。

答案 1 :(得分:5)

如果-[NSArray lastObject]自动释放该对象,则可以合理地问:“是否记录了自动释放对象,以便我可以依赖此行为?”

但如果-[NSArray lastObject] 自动释放该对象,则无需询问是否记录了该行为。如果您希望确保它超过removeLastObject或数组的释放,则必须保留返回的对象。如果你不保留它,你的程序会行为不端。文档说或不说的并不重要:在这种情况下确保程序正常运行的唯一方法是保留对象。如果他们将来更改lastObject以自动释放对象,那么您的程序仍然行为正确。

所以,除了别的什么,你应该保留这个对象。

但我们可以知道别的东西。我们可以反汇编-[NSArray lastObject]方法来确定它是否自动释放。然后我们就会知道我们是否应该打扰文档所说的内容。

-[NSArray lastObject]方法在CoreFoundation框架中实现。我使用Hopper

反汇编了OS X 10.8(Mountain Lion)64位版本
75e30 55              push       rbp
75e31 4889E5          mov        rbp, rsp
75e34 53              push       rbx
75e35 50              push       rax
75e36 4889FB          mov        rbx, rdi
75e39 488D3530971800  lea        rsi, qword [ds:objc_msg_count] ; @selector(count)
75e40 4889DF          mov        rdi, rbx
75e43 FF1527971800    call       qword [ds:objc_msg_count]     ; @selector(count)
75e49 4885C0          test       rax, rax
75e4c 7509            jne        0x75e57

75e4e 31C0            xor        eax, eax
75e50 4883C408        add        rsp, 0x8
75e54 5B              pop        rbx
75e55 5D              pop        rbp
75e56 C3              ret        

75e57 48FFC8          dec        rax           ; XREF=0x75e4c
75e5a 488B0D1F971800  mov        rcx, qword [ds:objc_msg_objectAtIndex_] ; @selector(objectAtIndex:)
75e61 488D3518971800  lea        rsi, qword [ds:objc_msg_objectAtIndex_] ; @selector(objectAtIndex:)
75e68 4889DF          mov        rdi, rbx
75e6b 4889C2          mov        rdx, rax
75e6e 4883C408        add        rsp, 0x8
75e72 5B              pop        rbx
75e73 5D              pop        rbp
75e74 FFE1            jmp        rcx

如果您说x86汇编程序,可以将其转换为这个简单的Objective-C代码:

- (id)lastObject {
    NSUInteger count = self.count;
    return count == 0 ? nil : [self objectAtIndex:count-1];
}

it is documented that objectAtIndex does not autorelease the object开始,我们可以看到-[NSArray lastObject]的实际实现不会自动释放对象。

这意味着文档说的并不重要:如果您希望程序正常运行,则必须保留该对象。

请注意,我并不是说您应该依赖实施细节。我只是使用实现来决定是否值得寻找文档。

答案 2 :(得分:3)

永远不要依赖返回+1 autoreleased对象的实现,除非明确记录,NSArray没有明确声明它是自动释放的。这样做的原因是它是实现定义的,行为可能会随着时间而改变。如果您需要一个对象生命周期延长超过返回它的对象的时间长度或者直到下一个运行循环,那么您应该retain它。

例如,lastObject方法可以在每个框架版本之间自由切换以下两种实现。虽然从第二个实现切换到第一个实现从来都不是一个好主意,但它仍然可能会破坏任何在删除最后一个对象,所有对象或释放数组之前没有保留最后一个对象的代码。

//implementation 1
- (id)lastObject
{
    if(self.length < 1)
        return nil;

    return [self objectAtIndex:self.length - 1];
}
//implementation 2
- (id)lastObject
{
    if(self.length < 1)
        return nil;

    return [[[self objectAtIndex:self.length - 1] retain] autorelease];
}

答案 3 :(得分:0)

如果是自动释放的,则是一个实施细节,无法可靠地回答。

lastObject返回你不拥有的对象。根据可可内存命名规则:只有名为'new或copy或allocate'的方法明确表示(IIRC那三个)返回新对象

请参阅:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html

答案 4 :(得分:-1)

实际上没有,为什么他们会自动释放这个对象?

通过调用lastObject,您只需获取最后一个对象。它仍然保存在数组中。 因此,数组肯定不应该释放对象,也不应该为它保留对象。

如果您创建一个对象,可能会修改它,然后您可以将它传递给另一个对象,而不必再担心它,自动释放会很有用。

情况并非如此,它只是告诉数组中的最后一个对象是什么,保留是接收它的对象的工作。