Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

Respectively they are just:

struct objc_class *

and:

struct objc_object *

Question #1:

objc_msgSend(id self, SEL _cmd);

id as far as I know is of type struct objc_object *

But when we call a class method, a class, which is of type struct objc_class *,

I would expect it to cause problem or shout out some kind of warning such as „Hey, wrong type here, my friend“.

But there isn’t.

  • Why?

This only serves to fulfill my curiosity, because even without fully understanding this, it doesn’t seem to cause me any trouble (so far). But I would like to dig deep and learn the fundamentals / „peculiarities".

Question #2:

Since there is no warning according to my experience (relates to Question #1), hence my not-so-sure assumption that they could perhaps be used interchangeably.

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

If yes:

  • Can you show me a scenario and a sample when / how / why we would need to use these interchangeably?

  • What would be the benefit (if any), disadvantages (if any) and „gotchas“ (things to watch out for; if any) doing so?

like image 347
Unheilig Avatar asked Jan 01 '14 23:01

Unheilig


2 Answers

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

This isn't really true, particularly since ObjC2, and definitely isn't a good way of thinking about it. The "struct" in question has a single field (isa) defined. That's it. It's a "struct" in name only.

Note that under ARC objects and structs are treated differently by the compiler. Objects pointers will get special handling (including nil-initialization) that a struct pointer will not. The compiler will also apply -> differently to an object than it does to a struct. A struct pointer must have a field with the name given after the ->. objc_object only has one field (isa). Since class hierarchies can be defined and redefined at runtime, the compiler cannot evaluate -> for an object at compile time as it does for a struct.

I make this point because in C++, objects and structs are just slightly different versions of the same thing, and you can easily swap between them. You cannot safely do that in ObjC. They're not nearly as similar.

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

This is because classes are objects. Objects are not defined in terms of being of type objc_object. They're defined in terms of having an isa field (it used to be a pointer, but now it might be a pointer, or it might not). This is part of the problem of assuming that the typedef is the important thing. It isn't. What matters is whether it acts like an object. Objective-C is a mostly duck-typed language. If it acts like an object, it's an object.

BTW, NSProxy also lives in this weird almost-an-object world. If you note its definition, it does not inherit from anything, but it does declare an isa field as its first ivar. That's the critical part of making it act like an object.

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

No. You can generally pass a class to anything that wants an object (since a class is an object), but you cannot pass an object to things that want a class.

like image 149
Rob Napier Avatar answered Oct 18 '22 09:10

Rob Napier


I find this in runtime.h file.

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;
};

Both objc_class and objc_object has isa variable, isa in objc_object is point to Class which describes the instance and stores instance method list. Isa in objc_class is point to Class too but it is meta-class which describes the class and stores class method list.

The link is useful to you.

I have simulate the condition.

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) ;
}

You can use struct B * instead of struct A * because compiler and linker place int a in the same relative address.

Why compiler doesn't complain when we call class method which pass struct objc_class * to the objc_msgSend(id self, SEL _cmd);, in my opinion the compiler takes account of it.

like image 32
KudoCC Avatar answered Oct 18 '22 10:10

KudoCC