我试图理解C和Objective-C在用法和语法上的一些不同之处.特别是,我想知道C和Objective-C中点运算符和箭头运算符的用法有什么不同(以及为什么).这里有一个简单的例子.

C Code:

// 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 Code:

// 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在两个程序中是如何相同的(即它是一个指向分数对象或结构的指针),为什么它们在访问属性时使用不同的语法呢?具体来说,在C中,numerator属性是用frac->numerator访问的,但在Objective-C中,它是用frac.numerator点运算符访问的.既然frac在两个程序中都是指针,为什么这些表达式不同呢?有人能帮我澄清一下吗?

推荐答案

frac在两个项目中实际上是不一样的.

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

然而,在您的示例中,Objective-CFraction可能是(可以假设)至少id类型的指针,它只是一个类名和指向该类实例的指针.它也很可能是NSObjectNSProxy的一个子类.这些Objective-C类的特殊之处在于,它们在C结构之上有一整层预定义的操作(如果你真的想深入研究它,那么你可以看看Objective-C Runtime Reference).同样需要注意的是,Objective-C类是always a pointer.

最基本的操作之一是objc_msgSend.当我们对这些类型的对象进行操作时,Objective-C编译器将点(.)运算符或方括号语法([object method])解释为objc_msgSend方法调用.有关这里实际发生的情况的更多详细信息,请参阅苹果工程师比尔·布姆加纳的this series of posts,他负责监督Obj-C运行时的开发.

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

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

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

你可以大致这样想:

Dot operator on a C object

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

Arrow operator on a C object (pointer)

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

Dot operator/square brackets on an Objective-C object (pointer)

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

Arrow operator on an Objective-C object (pointer)

  1. (at run time) 取消引用指针
  2. 字段的返回值

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

C++相关问答推荐

将C中的2D char数组写入/读取到二进制文件

C语言中的“对象”是什么?

强制转换为“超类”并返回 C 是否违反严格别名?

为什么将输出写入正在缓冲的管道

libcurl 放置数据流而不是文件

您可以使用 C 内联汇编来对齐指令吗? (没有编译器优化)

我可以有条件地定义一个 openMP 并行区域吗?

从两个 32 位定时器计数器读取 64 位定时器值时,正确的 ARM64(AArch64)数据内存屏障使用是什么?

C中的“x”和“&x[0]”有什么区别吗?

GNU inline asm:允许不同输出操作数的相同寄存器?

C中块内的函数声明

未定义值的索引处的数组值

为什么分段错误不可恢复?

在 Win32 上双重转换为 unsigned int 被截断为 2,147,483,648

如果缺少 const char* 数组初始化逗号,则生成编译器警告

从函数返回结构时可能出现 GCC 错误

我可以用 NULL 代替 0 的值吗?

对平方数字求和时是否需要明确处理负数或零?

为什么在启用 GCC 优化的情况下,这段代码使用 strlen 的速度要慢 6.5 倍?

“使用带二进制位运算符的有符号整数操作数” - 使用无符号短时