为什么使用id的时候我们只能使用NSObject?

问题描述:

我知道,当我们想创建一个未知的值对象时,我们使用id。然而,我很好奇苹果为什么选择在运行时决定它的值的id,当时每个对象都是NSObject的一个子类。所以,而不是id delegate我们可以用NSObject *delegate有谁知道为什么?谢谢。为什么使用id的时候我们只能使用NSObject?

+1

NSProxy是可可提供的另一个根类。 –

+0

NSObject包含isa指针,id不包含。看看http://*.com/a/19634973/944634 –

+0

什么大声笑? id是一个isa指针。 http://*.com/questions/1990695/in-cocoa-how-is-the-id-type-defined – TheAmateurProgrammer

id消除类型,它等同于说“此对象响应任何对翻译可见的选择器”。当然,它是你的责任,以确保你的程序是正确的,当你擦除类型(以及当你对它们进行类型化时)。

如果类型为NSObject,那么编译器会说“NSObject的可能不选择回应:”如果选择不是在NSObject的界面也采用了协议声明。在这种情况下,你也可以添加一个类型转换来将它转换为你期望的类型。

对于严格/正确的类型,编译器可以踢出来帮助你,这很好,因为ObjC是一种非常动态的语言。

id在使用(或构建)集合类型时特别有用。除非您定义了新的根类型(不从NSObject继承),否则添加对象不会成为问题。如果我们要将它用作基类(NSObject)以外的其他类型,那么从集合中获取值将需要一个类型转换。

Objective-C不支持泛型 - 例如,您不能声明NSArrayNSString s。您可以使用NSString填充NSArray s,并通过id传递此类型以获得更自然的书写样式,但不保留类型安全性(la泛型)。

所以,让我们用一些真实的代码来展开。

例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:

NSObject<MONShapeDelegate>* delegate; 
+0

其他编译器可能不同于我使用的编译器,但据我所知,如果你声明类型为NSObject ,编译器将假定该对象实现SomeProtocol的所有方法,即使是可选的方法。 – Aberrant

+0

@Aberrant如果委托方法是可选的,则由*调用方*来验证对象是否响应选择器。 – justin

+0

我知道,这就是为什么我说编译器假定它们已经实现。如果没有,可选项总是会导致警告,因为编译器无法确定对象是否会在运行时进行响应。 – Aberrant

每个对象都是NSObject的

的子类,这是不正确的。你可以创建一个从根本上没有任何东西的对象。

这也许就是为什么id被引入。

每个对象都是NSObject的

的子类,是不正确的陈述。您可以创建不从NSObject继承的对象。这不是真的推荐,但它是可能的。

NSProxy是一个例子 - 它不从NSObject继承。

+3

这只是我的答案的克隆! –

+1

抱歉:/我在没有答案的时候写下了它,你打败了我......没有难过的感觉,也没有任何需要投票的权利......另外,你没有提到'NSProxy' ... – Jasarien

+1

如果它是一个堆栈,这个答案在底部@Jasarien首先回答;) – beryllium

typedef struct objc_object { 
    Class isa; 
} *id; 

以上是Objective-C语言中id的实际定义。 Objective-C运行时系统围绕idClass构建。没有什么与NSObject或普通超类有关。

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocObjectsClasses.html#//apple_ref/doc/uid/TP30001163-CH11-SW3

NSObject类

NSObject的是根类,因此不具有父类。它定义了Objective-C对象和对象交互的基本框架 。 它赋予类继承自类的类和实例,它具有作为对象行为的能力并与运行时系统配合运行。

不需要继承任何来自另一个 类的特殊行为的类应该成为NSObject类的子类。 该类的实例至少必须能够在运行时像 Objective-C对象那样运行。继承这个从 开始的能力比使用新的类定义重新创建 要简单得多,也更可靠。

我认为,这是因为它原本是CC++(或其他更严格键入语言)。