13

最近的 SO讨论让我感到困惑。NSMutableArray 的原型addObject:

- (void)addObject:(id)anObject

id在 objc.h 中定义为

typedef struct objc_class *Class;
typedef struct objc_object {
    Class isa;
} *id; 

当我将 NSObject 或子类添加到 anNSMutableArray时,它的保留计数会增加,而当我从 an 中删除它时,NSMutableArray它会减少。这是否意味着如果将id type不是一个NSObject或子类的一个添加到一个NSMutableArray,它必须响应保留和释放消息?的定义id似乎并没有强迫这一点。任何人id type都应该响应标准内存管理消息是一个客观的 C 指令吗?

4

6 回答 6

7

大多数 Foundation 容器(以及大部分 Apple 开发的类,以及大部分由第三方开发的类)的硬道理是,当一个方法接受该id类型时,它应该真正读取id<NSObject>,这意味着任何响应NSObject协议。不属于NSObject层次结构的类的实例不太可能响应-retainand -release,这在尝试将它们添加到容器时尤其不方便。它们也不太可能响应-hash, -isEqual:, -description,-copy以及 Foundation 容器可以出于任何原因对其内容使用的所有其他方法。

例如,如果您尝试将Class对象添加到 Foundation 容器(除了NSMapTable因为这个容器的设计考虑到了很大的灵活性),您将碰壁,因为“现代”ObjC 类应该继承自NSObject,或最少执行NSObject协议。

不过,这是一种非常罕见的情况。Class几乎是唯一一个不继承自NSObject.

于 2011-10-26T15:37:47.257 回答
4

这是否意味着如果将一个不是 NSObject 或子类的 id 类型添加到 NSMutableArray 中,它必须响应保留和释放消息?

默认情况下,是的,尽管使用 CF-API 可以解决此问题。

id 的定义似乎并没有强迫这一点。任何 id 类型都应响应标准内存管理消息是客观的 C 指令吗?

这就是库的编写方式;根类(不继承自 NSObject)是非常不寻常的。一个替代方案可能是- (void)addObject:(id<NSObject>),但这需要对您的根类进行相当大的扩展......也许更好的解决方案是一个协议NSReferenceCounted,该协议从NSObject.

然而,NS-collections 类型确实假设它们正在处理 NSObjects(例如字典使用hashand description)。

于 2011-10-26T15:39:42.670 回答
2

不,没有约定“id”类型的对象应该响应保留/释放消息;事实上,有人可能会说保证这些方法的存在是 NSObject 协议(而不是类)的目的。但是,“id”确实告诉编译器“不要打扰类型检查”,因此当您将对象添加到未实现这些方法的 nsarray 时,它会编译,但您会遇到运行时崩溃。有关更详细的说明,请参阅http://unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs-id.html 。

于 2011-10-26T15:37:01.353 回答
0

您不应该将非对象类型放入 中NSArray,例如,如果您希望存储一个数组,CGRects那么您将它们包装在NSValue对象中并存储它们(这与CGPoint, CGSizeetc 相同)。

如果您有一个自定义结构,那么您将需要一个NSObject后代包装器,这首先违背了目的!

于 2011-10-26T15:34:17.313 回答
0

除了上面的技术讨论之外,我认为答案与语言的历史以及它对约定优于语法的偏好有关。正式的协议最初并不是语言的一部分——一切都是非正式的。Apple 随后大多转向正式协议,但非正式协议是语言的一部分,并被部分官方 API 使用。因此,它们是完全受支持的 Objective-C 的一流部分。

如果您查看 to 的文档NSArray,它会说,除其他外:

在大多数情况下,您的自定义 NSArray 类应该符合 Cocoa 的对象所有权约定。因此,您必须将保留发送到您添加到集合中的每个对象,并释放到您从集合中删除的每个对象。当然,如果继承 NSArray 的原因是为了实现与规范不同的对象保留行为(例如,非保留数组),那么你可以忽略这个要求。

和:

NSArray 与它的核心基金会对应物 CFArray 是“免费桥接” [...] 有时你可以用 CFArray 做一些你不能用 NSArray 轻易做到的事情。例如,CFArray 提供了一组回调,其中一些用于实现自定义保留释放行为。如果为这些回调指定 NULL 实现,则可以轻松获得非保留数组。

因此,您的问题基于一个错误的前提,特别是对文档的部分阅读或对语言的不完整考虑。

id <NSObject>未正确使用,因为该NSObject协议与对象必须实现才能与 . 一起使用的协议不匹配NSArray。相反,在文档中建立了一个非正式协议,它恰好是 的一个子集NSObject(尽管在第二次删除非正式性时,这种情况很少见)。尽管非正式协议越来越少见,但它们是有效的,而且还不是例外。

所以,我的回答的简短版本是:NSArray记录一个非正式协议,该协议与NSObject. 非正式协议没有用语法表示,因此id.

于 2011-10-27T11:15:29.677 回答
0

如果您确实需要一个包含您不希望保留/释放的项目的数组,您可能希望使用它来创建它CFArrayCreateMutable并传递给它适当的回调。(CFMutableArray免费桥接至NSMutableArray。)

于 2011-10-26T16:47:47.057 回答