最近的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
应该响应标准的内存管理消息吗?
答案 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(例如字典使用hash
和description
)。
答案 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
。