我知道当我们想要创建一个未知值对象时,我们使用id。但是,我很好奇Apple为什么选择id在运行时决定它的值,当每个对象都是NSObject的子类时。因此,我们可以使用id delegate
而不是NSObject *delegate
有人知道为什么吗?感谢。
答案 0 :(得分:39)
id
删除类型,它相当于说“此对象响应翻译可见的任何选择器”。当然,当你删除类型时(以及当你对它们进行类型转换时),确保你的程序是正确的是你的责任。
如果类型是NSObject
,那么如果选择器未在NSObject的接口或它采用的协议中声明,编译器会说“NSObject可能不响应选择器”。在这种情况下,您还可以添加类型转换以将其强制转换为您期望的类型。
使用严格/正确的类型,编译器可以启动并帮助你,这很好,因为ObjC是一种非常动态的语言。
id
在使用(或构建)集合类型时特别有用。除非您定义了新的根类型(不从NSObject继承),否则添加对象不会有问题。如果我们将它作为基类(NSObject)之外的其他东西使用,那么从集合中获取值将需要类型转换。
Objective-C不支持泛型 - 例如,您不能声明NSArray
NSString
的{{1}}。您可以使用NSArray
填充NSString
并通过id
传递此内容,以便在未保留类型安全性时使用更自然的书面样式(la generics)。
所以,让我们用一些真实的代码扩展它。
示例A
NSString * string = [array objectAtIndex:0]; // << trust me (via id)
return [string length];
-or-
return [[array objectAtIndex:0] length]; // << trust me (via id)
示例B
现在让我们说id
不可用,我们修复了所有编译器警告,因为这是正确的做法:
NSString * string = (NSString*)[array objectAtIndex:0]; // << typecast == trust me
return [string length];
-or-
return [(NSString*)[array objectAtIndex:0] length]; // << typecast == trust me
id
不会在运行时决定它的值,也不会决定任何NSObject。 ObjC对象不执行隐式促销,只是在没有正式升级的情况下投射指针。
与您的示例相关,我实际上将我的委托和参数声明为NSObjects with protocols:
NSObject<MONShapeDelegate>* delegate;
答案 1 :(得分:11)
每个对象都是NSObject的子类
这是一个不正确的陈述。您可以创建不从NSObject继承的对象。它并不是真的推荐,但它是可能的。
NSProxy
是一个例子 - 它不会从NSObject继承。
答案 2 :(得分:3)
typedef struct objc_object {
Class isa;
} *id;
以上是Objective-C语言中id
的实际定义。 Objective-C运行时系统围绕id
和Class
构建。没有什么与NSObject或普通超类有关。
NSObject类
NSObject是一个根类,因此没有超类。它定义了 Objective-C对象和对象交互的基本框架。 它赋予继承的类的类和实例 它具有表现为对象并与运行时协作的能力 系统
不需要从另一个继承任何特殊行为的类 尽管如此,class仍应成为NSObject类的子类。 该类的实例必须至少具有行为的能力 运行时的Objective-C对象。从。继承此功能 NSObject类比重新发明更简单,更可靠 它在一个新的类定义中。
我认为,这是因为它最初是C
而不是C++
(或其他更严格的输入语言)。
答案 3 :(得分:2)
每个对象都是NSObject的子类
这不正确。你可以创建一个从无任何扩展的对象作为根类。
这也许就是id被引入的原因。