【问题标题】:Need some clarification on id data type in Objective-C需要对 Objective-C 中的 id 数据类型进行一些说明
【发布时间】:2017-08-16 02:18:01
【问题描述】:

假设我在 Objective-C 中创建了一个 Fraction 类(如“使用 Objective-C 编程”一书)。其中一个方法 add: 最初是这样创建的:

//Fraction.h & most of Fraction.m left out for brevity's sake.
-(Fraction *)add: (Fraction*) f {
    Fraction *result = [[Fraction alloc] init];

    //Notice the dot-notation for the f-Fraction
    result.numerator = numerator * f.denominator + denominator * f.numerator;
    result.denominator = denominator * f.denominator;

    return result;
}

然后在后面的一个练习中,它说将返回类型和参数类型更改为 id 并使其工作。如上所示的点符号不再起作用,所以我将其更改为:

-(id)add: (id)f {
    Fraction *result = [[Fraction alloc] init];

    result.numerator = numerator * [f denominator] + denominator * [f numerator];
    // So forth and so on...
    return result;
}

现在我猜测为什么需要更改点表示法是因为直到运行时,程序不知道传递给添加参数(f)的对象类型,因此编译器不知道'不知道 f 的任何访问器方法。

我是否接近理解这一点?如果不是,有人可以澄清一下吗?

【问题讨论】:

  • 即使您可以将id f 类型转换为Fraction 并使用.

标签: objective-c syntax


【解决方案1】:

点语法和属性实际上完全没有任何关系,除了它们是同时引入的。

属性便于在类上定义访问器,同时还增加了指定原子性策略和/或内存管理策略的能力。

点语法便于访问对象上的任何类似访问器的 API。点不限于与属性一起使用;例如,你可以说myArray.length

在创建点语法时,希望尽可能地限制歧义。因此,特别选择使用点将要求表达式中的对象被显式键入; id 泛型类型是不允许的。

动机是 Key-Value Coding 类型的表达式在各种程序中爆炸的次数。由于点是一种编译后的 KVC 表达式,因此目标是消除这种脆弱性。


一般来说,id 类型是要避免的。通过引入 instancetype 关键字(与原始问题完全无关,但与上述讨论相关)进一步鼓励了这一点。

【讨论】:

  • 我仍然认为应该能够在ids 上使用点语法。我正在用 Objective-C 编写 Bison 语法,它会使代码更清晰/更清晰。 :'(
  • @PauloTorrens 虽然这会使语法创建更容易,但支持该语言的解析器被认为是支持该语言用户的次要因素。 ;)
  • @bburn 实际上,解析器使用语言。它不是一个 Objective-C 解析器,但它是用它编写的。无论如何......它是有道理的(至少对我来说),对于一般的类类型,即使对于NSObject,如果没有声明属性或消息,它会引发语法错误。但是有什么理由不能在id 类型上忽略这一点,接受点后的任何消息?在某些情况下,它可以使代码更简洁以避免强制转换,并且当您确定它可以在运行时工作时仍然使用点语法(尽管我理解编码人员会轻松滥用此功能)。
【解决方案2】:

这是正确的。编译器无法在编译时确定对象的实际类型,因此无法确定denominator 是否为属性。将其更改为使用括号语法允许它将denominator 作为消息发送到对象,而无需知道对象类型。唯一需要让它编译而不会出错的是方法-denominator 被定义anywhere。可以想象,使用id 并避免类型检查是危险的,因为您可能会向未实现该方法的对象发送消息,从而导致引发运行时异常。

编辑:作为bbum points outdenominator 不必是属性,这是对使用id 泛型类型的对象使用点语法的一般限制。

【讨论】:

  • 我认为练习的重点是了解使用 id 数据类型时发生了什么。我知道过度使用 id 可能不是最佳做法——尤其是当您知道参数需要的类型时。
  • 几乎正确; denominator 不必是属性。事实上,属性和点语法除了在引入的时候没有任何关系。
  • AFAIK foo.bar 就编译器而言与[foo bar] 相同,这是真的还是假的?
  • 是的,这是真的。编辑我的帖子以添加 bbum 的注释。
  • @11684 只要foo是强类型并声明了一个名为notAsPropertyDeclaredBar的方法,那么你就对了。 Josh 可以对访问器起别名是正确的,但这与简单地将 peanutButter 声明为返回 jelly 的方法(在他的示例中)没有什么不同。
【解决方案3】:

@bbum 是对的,简单来说,如果你有一个类型为“id”的对象“obj”并调用一个带有点语法的方法,例如:obj.methodName,编译器将检查类型“obj”,并产生错误:"Property 'methodName' not found",而[obj methodName] 将绕过编译器检查。稍后它可能会在运行时崩溃,例如如果“methodName”拼写错误。查看文章中的更多示例,阐明Objective C id type checking 和其他 id 行为。

请注意,点语法仅限于没有参数的方法,因此像 [objc setStuff:123] 这样的调用不会被选中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2013-07-17
    • 2019-04-03
    • 1970-01-01
    • 2016-06-16
    • 2014-10-02
    • 1970-01-01
    相关资源
    最近更新 更多