在Objective C中,id和NSObject有什么不同?

13
这两者有何不同?

id:

#import <objc/Object.h>

@interface Forwarder : Object
{
    id something;
}

NSObject:

#import <objc/Object.h>

@interface Forwarder : Object
{
    NSObject *something;
}

谢谢你。


1
完全披露:在发布答案后才意识到这个问题可能是来自于https://dev59.com/a3RB5IYBdhLWcg3w6bcE的重复。 - VonC
3个回答

17

这是Greg Miller的博客文章,来自unixjunkie博客,总结了以下差异:

以下是一些摘录:

在Objective-C中,以下三种声明常常会引起混淆:

id foo1;
NSObject *foo2;
id<NSObject> foo3;

第一种是最常见的。它只是声明了一个指向某个 Objective-C 对象的指针(参见 /usr/include/objc/objc.h)。id 不会给编译器提供关于对象实际类型的信息,因此编译器无法为您进行编译时类型检查。
仅仅因为我们知道 id 是 Objective-C 对象并不意味着它指向的对象派生自 NSObject,或者它甚至具有像 retain 和 release 这样的常用方法。其中一个解决方案是使用 NSObject* 静态类型化变量,如上面的第2个示例所示。这样可以让编译器了解 foo2 指向的对象的类,因此如果您向 foo2 发送 NSObject 不能响应的消息,则编译器会发出警告。这意味着您可以安全地调用 retain、release、description 等方法,但如果调用 length 或 count 或任何 NSObject 不能响应的方法,则编译器会发出警告。
将对象声明为 id 可以告诉编译器您不关心对象的类型,但您确实关心它是否符合指定的 NSObject 协议**。编译器将确保分配给该指针的所有对象都符合所需的协议。像这样类型化的指针可以安全地持有任何 NSObject(因为 NSObject 符合 NSObject 协议),但也可以持有任何 NSProxy,因为 NSProxy 也符合 NSObject 协议。用英语来说,声明 id foo3; 的意思是 "foo3 是指向任何行为类似于 NSObject 的类型的对象的指针"。这非常强大、方便和表达力强。实际上,我们通常不关心对象的类型,只关心它是否响应我们想要发送的消息(例如 retain、release)。
如果您不想(或无法)进行任何类型检查,那么使用一个普通的 id。对于不知道返回对象类型的方法(例如+alloc)返回类型非常常见。还有通常将委托声明为类型id,因为委托通常在运行时使用respondsToSelector:进行检查,并且它们通常不会被保留。
然而,如果您需要编译时类型检查,您必须在第二个和第三个情况之间做出决定。好吧,让我帮你——你需要第三种情况!:-) 我很少看到NSObject *可行但id不行的情况。并且使用协议形式的优点是它将与NSProxys一起工作。

在我看来,respondsToSelector选择器在第三种情况下不起作用。那么我会建议使用第二种情况。 - user4951

3
实际上的区别是,在使用一个id时不需要进行类型转换,但通常在使用NSObject *之前需要将其转换为某种类型。 NSObject是几乎所有其他类都派生自的基类,而id更像是一种语言关键字。

1

一个id可以响应任何方法而不会出现编译警告;NSObjects只能响应NSObject中定义的方法,包括NSObject协议。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接