关于iOS / Objective-C:iOS / Objective-C – struct objc_class *和struct objc_object *

iOS / Objective-C - struct objc_class * and struct objc_object *

我的理解是Objective-C中的对象和类只是结构。

他们分别只是:

1
struct objc_class *

和:

1
struct objc_object *

问题#1:

1
objc_msgSend(id self, SEL _cmd);

id据我所知,类型为struct objc_object *

但是当我们调用一个类方法时,一个类型为struct objc_class *的类,

我希望它会引起问题或者喊出某种警告,例如?嘿,这里的错误类型,我的朋友"。

但事实并非如此。

  • 为什么?

这只是为了满足我的好奇心,因为即使没有完全理解这一点,它似乎也没有给我带来任何麻烦(到目前为止)。但我想深入挖掘并了解基本面/"特点"。

问题2:

由于根据我的经验没有警告(与问题#1有关),因此我不太确定它们可以互换使用。

struct objc_class *struct objc_object *真的可以互换使用吗?

如是:

  • 你能告诉我一个场景和样本何时/如何/为什么我们需要互换使用这些?

  • 什么是好处(如果有的话),缺点(如果有的话)和?陷阱"(需要注意的事项;如果有的话)这样做?


My understanding is that objects and classes in Objective-C are just structs.

这不是真的,特别是因为ObjC2,并且绝对不是一个好的思考方式。所讨论的"结构"具有定义的单个字段(isa)。而已。它只是名义上的"结构"。

请注意,在ARC对象下,编译器会对结构进行不同的处理。对象指针将获得结构指针不会的特殊处理(包括nil-initialization)。编译器还将对对象应用->,而不是对结构应用->。结构指针必须具有在->之后给出名称的字段。 objc_object只有一个字段(isa)。由于可以在运行时定义和重新定义类层次结构,因此编译器无法在编译时为对象评估->,就像对结构一样。

我提出这一点是因为在C ++中,对象和结构只是同一个东西的略有不同的版本,你可以轻松地在它们之间交换。你不能在ObjC中安全地做到这一点。它们几乎没有相似之处。

But when we call a class method, a class, which is of type struct objc_class *, I would expect it to cause problem…

这是因为类是对象。对象不是根据类型objc_object定义的。它们是根据isa字段定义的(它曾经是一个指针,但现在它可能是一个指针,或者它可能不是)。这是假设typedef是重要的问题的一部分。事实并非如此。重要的是它是否像一个物体。 Objective-C是一种主要是鸭式的语言。如果它像一个对象,它就是一个对象。

BTW,NSProxy也生活在这个奇怪的近乎对象的世界里。如果你注意它的定义,它不会从任何东西继承,但它确实声明一个isa字段作为它的第一个ivar。这是让它像对象一样行动的关键部分。

Can struct objc_class * and struct objc_object * really be used interchangeably?

不可以。您通常可以将类传递给任何想要对象的东西(因为类是对象),但是您不能将对象传递给想要类的东西。


我在runtime.h文件中找到它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;


struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

objc_class和objc_object都有isa变量,objc_object中的isa指向描述实例并存储实例方法列表的Class。 objc_class中的Isa也指向Class,但它是描述类并存储类方法列表的元类。

该链接对您有用。

我已经模拟了病情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct A {
    int a ;
} ;

typedef struct A *PA ;

struct B {
    int a ;
    int b ;
} ;

void dosomething(PA pa)
{
    printf("%d", (*pa).a) ;
}

+ (void)hei
{
    struct B b ;
    b.a = 10 ;
    b.b = 20 ;
    // without forced cast, i will get a warning but work well
    dosomething((PA)&b) ;
}

您可以使用struct B *而不是struct A *,因为编译器和链接器将int a放在相同的相对地址中。

当我们调用将struct objc_class *传递给objc_msgSend(id self, SEL _cmd);的类方法时,为什么编译器不会抱怨,在我看来编译器会考虑它。