4

我想知道该语言的面向对象特性是如何在 C 中实现的。

如 中所定义<objc/runtime.h>idtype 是一个指向 a 的指针objc_objectobjc_objecttype 是一个只有一个成员isa存储 a的 C 结构Class。那么任何对象的实例变量的实际值存储在哪里以及如何存储?

还有一件事,所有 Objective-C 对象指针都可以转换为id(这是一个指向 C 结构的指针,其中不存在继承等特性),Objective-C 类只是编译器的限定符,并且所有实例的类型相同objc_object

添加:

NSObject *obj = [NSObject new];
objc_object *objStruct = (__bridge objc_object *)obj;
NSLog(@"obj: %@", NSStringFromClass(objStruct->isa));

NSString *str = [NSString new];
objc_object *strStruct = (__bridge objc_object *)str;
NSLog(@"str: %@", NSStringFromClass(strStruct->isa));

此代码编译并输出:

obj: NSObject, str: __NSCFConstantString

两者objstr都可以转换为objc_object *,这意味着两个变量都是相同类型的指针,不是吗?

解决

明白了!我误解了指针转换的工作原理。objstr是不同结构类型的指针,但两者通常isa在内存前面都有类类型成员,因此可以视为objc_object. 下面的代码模仿了这种机制:

typedef struct {
    int isa;
} Fake_NSObject;

typedef struct {
    int isa;
    char *string;
} Fake_NSString;

Fake_NSObject obj = {1};
Fake_NSObject *objPtr = &obj;
NSLog(@"obj: %d", objPtr->isa); // prints 1

Fake_NSString str = {2, "abc"};
Fake_NSString *strPtr = &str;
NSLog(@"str: %d", strPtr->isa); // prints 2

Fake_NSObject *objPtr2 = (Fake_NSObject *)strPtr; // this is ok.
NSLog(@"obj2: %d", objPtr2->isa); // prints 2

Fake_NSString *strPtr2 = (Fake_NSString *)objPtr; // this is invalid, but still works.
NSLog(@"str2: %d", strPtr2->isa); // prints 1

谢谢!

4

1 回答 1

5

那么它的实例变量的实际值在哪里以及如何存储?

它们像往常一样存储在同一个地方——在对象内部。正如您所说,id表示指向任何类型对象的指针。但是,除此之外,它没有定义任何特定类型——它没有说明对象的实际类型。

objc_object只是一个基本类型,相当于NSObject. 看看 NSObject.h,你会发现 的实例NSObject有一个实例变量,即isa指针。也是如此NSProxy,它是 Objective-C 中的另一个根类。NSObjector的子类NSProxy可以添加它们自己的实例变量,这些实例变量附加到父类的结构中。

如果所有 Objective-C 对象指针都可以转换为 id,那么所有 Objective-C 类是否都只是编译器的限定符,并且在运行时所有这些实例都是同样类型的 objc_object?

我不确定你在这里问什么。编译器不会阻止你向任何对象发送任何消息(尽管如果它认为你正在向不支持它的对象发送消息,它会警告你),所以从某种意义上说编译器并不关心关于各种对象类型。另一方面,不同类型的对象不同的——并非所有类都转换为通用类型。

id强烈类似于void *void *是一个通用指针,您可以将任何指针强制转换为void *,但这并不意味着所有指针都是等价的。与分配给类型变量的指针必须指向Objective-C对象id的附加限制几乎相同。id

于 2013-05-28T04:18:18.753 回答