66

我试图围绕 C 与 Objective-C 在用法和语法方面的一些差异。特别是,我想知道 C 与 Objective-C 中点运算符和箭头运算符的用法有何不同(以及为什么)。这是一个简单的例子。

C代码:

// declare a pointer to a Fraction
struct Fraction *frac;

...

// reference an 'instance' variable
int n = (*frac).numerator;     // these two expressions
int n = frac->numerator;       // are equivalent

Objective-C 代码:

// declare a pointer to a Fraction
Fraction *frac = [[Fraction alloc] init];

...

// reference an instance variable
int n = frac.numerator;        // why isn't this (*frac).numerator or frac->numerator??

那么,看看frac两个程序中的相同之处(即它是一个指向 Fraction 对象或结构的指针),为什么它们在访问属性时使用不同的语法?特别是,在 C 中,使用numerator访问属性frac->numerator,但在 Objective-C 中,使用点运算符访问属性,使用frac.numerator。既然frac是两个程序中的指针,为什么这些表达式不同?谁能帮我澄清一下?

4

5 回答 5

87

frac实际上在两个程序中并不相同。

ACFraction是 a struct,它是没有重载运算符的基本类型,并且只能在默认情况下真正构造和销毁。如果您在结构上定义函数或字段,则访问这些属性的方法C是使用点 ( .) 运算符。struct当你使用s时,Objective-C 会维护这个操作符。->为方便起见,您可以使用箭头 ( ) 运算符(您提到的两个等效表达式)执行取消引用和点操作。structObjective-C 在访问s时也保留了这一点。

但是,您的示例中的 Objective-CFraction可能(人们会假设)至少是 type 的指针id,它只是一个类名和指向该类实例的指针。它也很可能是NSObjector的子类NSProxy。这些 Objective-C 类的特殊之处在于它们在结构之上有一整层预定义的操作C(如果你真的想深入研究它,那么你可以看看Objective-C 运行时参考)。同样重要的是要注意,Objective-C 类始终是一个指针

最基本的操作之一是objc_msgSend. 当我们对这些类型的对象进行操作时,Objective-C 编译器会将点 ( .) 运算符或方括号语法 ( [object method]) 解释为objc_msgSend方法调用。有关此处实际发生的情况的更多详细信息,请参阅Bill Bumgarner 的这一系列帖子,他是一位负责监督 Obj-C 运行时开发的 Apple 工程师。

箭头 ( ->) 运算符实际上不应该用于 Objective-C 对象。就像我说的,Objective-C 类实例是一个 C 结构,添加了额外的通信层,但是当您使用箭头时,该通信层基本上被绕过了。例如,如果你打开 Xcode 并输入,[UIApplication sharedApplication]->然后调出方法完成列表,你会看到:

使用箭头运算符时 Obj-C 对象上的内部 ivars 列表

在这里,您可以看到一堆我们通常使用方括号语法(如[[UIApplication sharedApplication] delegate])访问的普通字段。然而,这些特定的项目是C存储它们各自的 Objective-C 属性值的字段。

所以,你大致可以这样想:

C 对象上的点运算符

  1. (在运行时)字段的返回值

C 对象(指针)上的箭头运算符

  1. 取消引用指针
  2. 字段的返回值

Objective-C 对象(指针)上的点运算符/方括号

  1. (在编译时)替换为调用objc_msgSend
  2. (在运行时)查找 Obj-C 类定义,如果出错则抛出异常
  3. 取消引用指针
  4. 字段的返回值

Objective-C 对象(指针)上的箭头运算符

  1. (在运行时)取消引用指针
  2. 字段的返回值

现在我在这里肯定是过于简单化了,但总结一下:箭头运算符在两种情况下似乎做的事情基本相同,但点运算符在 Objective-C 中具有额外/不同的含义。

于 2012-01-31T01:09:28.287 回答
9

点符号是一种设计选择。由于我们总是处理指向 objc 实例的指针,我猜设计师想要一些熟悉的东西,这也不会破坏现有的程序。它是在几年前的 ObjC 2 中引入的。在此之前,您总是必须使用方括号进行消息传递。

点符号虽然有所不同 - 它不是直接访问,而是一条消息

那是:

obj.property = val;
// is the same as:
[obj setProperty:val];
// and not:
obj->property = val;


val = obj.property;
// is the same as:
val = [obj property];
// and not:
val = obj->property;

您仍然可以写入obj->ivar以访问指向对象成员的指针(如果可见)。

于 2012-01-31T00:18:11.470 回答
5

在您的第一个示例中,Fraction是一个结构。在您的第二个示例中,Fraction是一个 Objective-C 类(在 iOS 中可能是 的子类NSObject)。

C++不允许重载operator .. 因此,如果没有其他信息,您可以推断出您看到的点符号是集成到 Objective-C 中的附加语言结构,而不是 C/C++ 定义或重载的运算符。

碰巧,点表示法只是实现者选择作为属性访问的简写的设计特征,完全等同于方括号 getter:

myObjCVar.prop == [myObjCVar prop];
于 2012-01-31T00:18:52.420 回答
2

对象上的点运算符是一种用于访问对象属性的特殊语法。它在幕后调用属性的 getter 或 setter。因此,例如,[@"hello" length]@"hello".length是等价的*。对于所有其他类型,点与 C 点相同,箭头始终相同。

* 注意:访问器方法的名称并不总是与属性相同。如果它是一个已声明的属性并且该声明指定了一个特殊的 getter 或 setter 方法,则将使用该方法。

于 2012-01-31T00:18:30.473 回答
1

C 中的点和箭头符号与 Objective-C 中的相同(的严格超集)。我认为需要区分的根本区别是结构和Objective-C对象之间的区别。

在 Objective-C 中用于对象的点表示法用于在 Objective-C 2.0 中引入的属性。但是,对于结构,Objective-C 和 C 之间的 -> 和点表示法是相同的。

于 2012-01-31T00:22:00.047 回答