将非NSObject添加到NSMutableArray

时间:2011-10-26 15:30:05

标签: objective-c ios nsarray foundation reference-counting

最近的SO discussion使我感到困惑。 addObject:的NSMutableArray原型是

- (void)addObject:(id)anObject

id在objc.h中定义为

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

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

6 个答案:

答案 0 :(得分:7)

大多数Foundation容器(以及大多数Apple开发的类,以及大多数类由第三方开发的程度)的真实情况是,当方法接受id类型时,它应该真正读取{ {1}},表示响应id<NSObject>协议的任何类型。不属于NSObject层次结构的类的实例不太可能响应NSObject-retain,这在尝试将它们添加到容器时尤其不方便。他们也不太可能回复-release-hash-isEqual:-description以及基金会容器因其他原因可以在其内容上使用的所有其他方法。< / p>

例如,如果您尝试将-copy个对象添加到Foundation容器(Class以外的其他容器,因为这个容器设计时考虑到了很多灵活性),您会遇到问题。 “现代”ObjC类应该从NSMapTable继承,或者至少实现NSObject协议。

但这是一种非常罕见的情况。 NSObject几乎是唯一有用的类,不会从Class继承。

答案 1 :(得分:4)

  

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

默认情况下,是,虽然有使用CF-API的解决方法。

  

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

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

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

答案 2 :(得分:2)

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

答案 3 :(得分:0)

您不应该将非对象类型放在NSArray中,例如,如果您希望存储CGRects数组,那么将它们包装在NSValue个对象和存储中那些(CGPoint, CGSize等也是如此)。

如果你有一个自定义结构,那么你将需要一个NSObject后代包装器,这首先会破坏目的!

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

如果您查看NSArray的文档,它会说:

  

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

  

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

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

id <NSObject>未正确使用,因为NSObject协议与对象必须实现的协议不匹配才能与NSArray一起使用。相反,恰好是NSObject的子集的非正式协议在文档中建立(虽然是第二次删除非正式性,这是罕见的)。虽然非正式协议越来越少见,但它们是有效的,并不是特例。

因此,我的答案的简短版本是:NSArray记录非正式协议,该协议与NSObject不同。非格式协议未在语法中表示,因此id