当我们只能使用 NSObject 时,为啥还要使用 id?

Posted

技术标签:

【中文标题】当我们只能使用 NSObject 时,为啥还要使用 id?【英文标题】:Why use id when we can just use NSObject?当我们只能使用 NSObject 时,为什么还要使用 id? 【发布时间】:2011-12-15 18:42:53 【问题描述】:

我知道当我们想要创建一个未知的值对象时,我们会使用 id。但是,我很好奇为什么 Apple 选择 id 来决定它在运行时的值,当每个对象都是 NSObject 的子类时。所以我们可以使用NSObject *delegate 而不是id delegate 有人知道为什么吗?谢谢。

【问题讨论】:

NSProxy 是 cocoa 提供的另一个根类。 NSObject 包含 isa 指针,id 没有。看看***.com/a/19634973/944634 什么哈哈? id 是一个 isa 指针。 ***.com/questions/1990695/… 【参考方案1】:

id 删除类型,相当于说“此对象响应任何对翻译可见的选择器”。当然,有责任确保您的程序在擦除类型时(以及在类型转换时)是正确的。

如果类型是NSObject,那么如果选择器没有在 NSObject 的接口或它采用的协议中声明,编译器会说“NSObject 可能无法响应 selector”。在这种情况下,您还可以添加一个类型转换以将其转换为您期望的类型。

使用严格/正确的类型,编译器可以启动并帮助您,这很棒,因为 ObjC 是一种非常动态的语言。

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

Objective-C 不支持泛型 - 例如,您不能声明 NSArrayNSStrings。您可以使用NSStrings 填充NSArray,并将其传递给id,以便在不保留类型安全(如泛型)时获得更自然的书写风格。

所以,让我们用一些真实的代码对此进行扩展。

示例 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;

【讨论】:

它在其他编译器中可能与我使用的不同,但据我所知,如果您将类型声明为 NSObject,编译器将假定该对象实现了 SomeProtocol 的所有方法,甚至是可选的。 @Aberrant 如果委托方法是可选的,则由 调用者 来验证对象是否响应选择器。 我知道,这就是为什么我说编译器假定它们已实现。如果没有,可选值总是会导致警告,因为编译器无法确定对象是否会在运行时响应。 啊,好吧 - 我第一次阅读时误解了你的观点。 >> "此对象响应翻译可见的任何选择器" 【参考方案2】:

每个对象都是 NSObject 的子类

这是一个错误的说法。您可以创建不继承自 NSObject 的对象。不是很推荐,但有可能。

NSProxy 是一个示例 - 它不继承自 NSObject。

【讨论】:

这只是我的答案的克隆! 对不起:/我在没有答案的时候写了它,你打败了我......没有难过的感觉,也不需要投反对票......另外,你没有提到NSProxy... 如果它是一个堆栈并且这个答案在底部,那么@Jasarien 首先回答;) @beryl:答案的默认显示顺序按投票总数进行分类,然后在每一层中随机排列。如果您真的想知道确切的发布时间,“X 小时前已回答”附带了一个工具提示,它将为您提供时间戳。您还可以点击答案部分顶部的“最旧”选项卡,按发布日期对答案进行排序。 在将近七年后也许不值得一提,但是:回答问题并不是纠正问题中事实错误的正确方法。你应该评论这个问题,甚至重写它。【参考方案3】:
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 对象。 从 NSObject 类比重新发明更简单,更可靠 它在一个新的类定义中。

我认为,这是因为它最初是 C 而不是 C++(或其他更严格的打字语言)。

【讨论】:

【参考方案4】:

每个对象都是 NSObject 的子类

这是不正确的。您可以创建一个从无到有的对象作为根类。

这也许就是引入 id 的原因。

【讨论】:

以上是关于当我们只能使用 NSObject 时,为啥还要使用 id?的主要内容,如果未能解决你的问题,请参考以下文章

当我们可以不用 BETWEEN 运算符时,为啥还要使用它呢?

当我们可以在 awakeFromNib 中转储所有内容时,为啥还要使用 init(coder)?

当我们已经有 CROSS_COMPILE= 时为啥还要有 ARCH=

如果我们有 GPGPU,为啥还要使用 SIMD? [关闭]

当我们已经拥有更强大的向量时,为啥还需要堆栈?

为啥我们不能在不从 NSObject 继承类的情况下迅速采用协议?