chr*_*yal 48 pointers objective-c
在Objective-C中,为什么[object doSomething]?不是[*object doSomething]因为你在对象上调用一个方法吗?这意味着你应该取消引用指针?
bbu*_*bum 113
答案回到了Objective-C的C根源.Objective-C最初是作为C的编译器预处理器编写的.也就是说,Objective-C没有编译得那么多,因为它被转换成直接C然后编译.
从类型的定义开始id.它被声明为:
typedef struct objc_object {
Class isa;
} *id;
Run Code Online (Sandbox Code Playgroud)
也就是说,a id是指向第一个字段类型为Class的结构的指针(它本身是指向定义类的结构的指针).现在,考虑一下NSObject:
@interface NSObject <NSObject> {
Class isa;
}
Run Code Online (Sandbox Code Playgroud)
请注意,NSObject指向的类型的布局和布局id是相同的.这是因为,实际上,Objective-C对象的实例实际上只是指向结构的指针,该结构的第一个字段 - 总是一个指针 - 指向包含该实例的方法的类(以及一些其他元数据) ).
当您继承NSObject并添加一些实例变量时,无论是出于所有意图和目的,只需创建一个新的C结构,其中包含您的实例变量作为该结构中的插槽,并连接在所有超类的实例变量的插槽中.(现代运行时的工作方式略有不同,因此超类可以附加ivars,而不需要重新编译所有子类).
现在,考虑这两个变量之间的区别:
NSRect foo;
NSRect *bar;
Run Code Online (Sandbox Code Playgroud)
(NSRect是一个简单的C结构 - 不涉及ObjC). foo是使用堆栈上的存储创建的.一旦堆栈帧关闭,它将无法生存,但您也不必释放任何内存. bar是对NSRect结构的引用,该结构很可能是使用在堆上创建的malloc().
如果你试着说:
NSArray foo;
NSArray *bar;
Run Code Online (Sandbox Code Playgroud)
编译器会抱怨第一个,在Objective-C中不允许使用基于堆栈的对象.换句话说,所有 Objective-C对象必须从堆中分配(或多或少 - 有一两个例外,但它们对于这个讨论比较深奥),因此,你总是通过一个对象来引用一个对象堆上所述对象的地址; 你总是使用指向对象的指针(而id类型实际上只是指向任何旧对象的指针).
回到语言的C预处理器根,您可以将每个方法调用转换为等效的C行.例如,以下两行代码是相同的:
[myArray objectAtIndex: 42];
objc_msgSend(myArray, @selector(objectAtIndex:), 42);
Run Code Online (Sandbox Code Playgroud)
同样,一个声明如下的方法:
- (id) objectAtIndex: (NSUInteger) a;
Run Code Online (Sandbox Code Playgroud)
相当于C函数声明如下:
id object_at_index(id self, SEL _cmd, NSUInteger a);
Run Code Online (Sandbox Code Playgroud)
并且,看一下objc_msgSend(),第一个参数被声明为类型id:
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...);
Run Code Online (Sandbox Code Playgroud)
这正是您不使用*foo方法调用的目标的原因.通过上面的表单进行[myArray objectAtIndex: 42]转换- 调用转换为上面的C函数调用,然后必须使用等效的C函数调用声明调用某些东西(所有这些都用方法语法编写).
引用对象引用因为它给了messenger - objc_msgSend()访问类然后找到方法实现 - 以及那个引用然后成为第一个参数 - 自我 - 最终的方法执行.
如果你真的想深入,请从这里开始.但是在你完全理解之前不要打扰.
Ben*_*tto 15
你不应该真正将它们看作指针到对象.这是一个历史实现细节,它们是指针,并且你在消息发送语法中使用它们(参见@bbum的答案).实际上,它们只是"对象标识符"(或引用).让我们回顾一下,看看概念的基本原理.
Objective-C首先在本书中提出并讨论:面向对象编程:一种进化方法.对于现代Cocoa程序员来说,它并不是非常实用,但语言的动机就在那里.
请注意,在本书中,所有对象都是类型id.你根本没有看到Object *书中更具体的内容; 当我们谈论"为什么"时,这些只是抽象的漏洞.这是本书的内容:
对象标识符必须唯一地标识在任何时候在系统中共存的对象.它们存储在局部变量中,作为参数传递给消息表达式和函数调用,保存在实例变量(对象内的字段)和其他类型的内存结构中.换句话说,它们可以像基本语言的内置类型一样流畅地使用.
对象标识符如何实际标识对象是一个实现细节,许多选择都是合理的.合理的选择,当然是最简单的选择,也是Objective-C中使用的选择,是使用内存中对象的物理地址作为其标识符.Objective-C通过在每个文件中生成一个typedef语句使C知道这个决定.这根据C已经理解的另一种类型(即指向结构的指针)定义了一个新类型id.[...]
id占用固定的空间量.[...]此空间与对象本身中私有数据占用的空间不同.
(第58-59页,第2版)
所以你的问题的答案是双重的:
严格类型的语法,你说"一个特定于NSString类型的对象",因此使用NSString *是一个更现代的变化,基本上是一个实现选择,相当于id.
如果这似乎是对指针解除引用问题的高度回应,请务必记住,根据语言的定义,Objective-C中的对象是"特殊的".它们被实现为结构并作为指向结构的指针传递,但它们在概念上是不同的.
Dar*_*ren 10
因为objc_msgSend()声明如下:
id objc_msgSend(id theReceiver, SEL theSelector, ...)
Run Code Online (Sandbox Code Playgroud)
你从不取消引用对象指针,句点.它们被键入为指针而不仅仅是"对象类型"的事实是语言C传承的工件.它完全等同于Java的类型系统,其中始终通过引用访问对象.你永远不会在Java中取消引用一个对象 - 事实上,你不能.您不应该将它们视为指针,因为从语义上讲,它们不是.它们只是对象引用.