Andrey Chernukha
Andrey Chernukha

Reputation: 21808

Understanding Objective-C runtime

This is an excerpt from Objective-C runtime programming guide:

When a new object is created, memory for it is allocated, and its instance variables are initialized. First among the object’s variables is a pointer to its class structure. This pointer, called isa, gives the object access to its class and, through the class, to all the classes it inherits from.

isa is declared in NSObject like this:

Class   isa;

In its turn Class is nothing more than a pointer to the struct

typedef struct objc_class *Class;

And now let's look at this structure:

struct objc_class {
Class isa;

#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

}

We can see that the pointer to the super class (as well as all the rest of the struct's members except another one isa) is unavailable in the latest version of Objective-C.

So my question is how an object can get access to its superclass if super_class pointer is not available? Does it get access to the superclass through this another one isa pointer? But how exactly it happens? How it works? Can anyone explain it?

Upvotes: 6

Views: 2711

Answers (2)

Bryan Chen
Bryan Chen

Reputation: 46568

just checked the source file

objc-class.m

Class class_getSuperclass(Class cls)
{
    return _class_getSuperclass(cls);
}

runtime-new.mm

#define newcls(cls) ((class_t *)cls)

Class 
_class_getSuperclass(Class cls)
{
    return (Class)getSuperclass(newcls(cls));
}

static class_t *
getSuperclass(class_t *cls)
{
    if (!cls) return NULL;
    return cls->superclass;
}

so Class is actually a pointer to class_t

objc-runtime-new.h

typedef struct class_t {
    struct class_t *isa;
    struct class_t *superclass;
    Cache cache;
    IMP *vtable;
    uintptr_t data_NEVER_USE;  // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() const { 
        return (class_rw_t *)(data_NEVER_USE & ~(uintptr_t)3); 
    }
    void setData(class_rw_t *newData) {
        uintptr_t flags = (uintptr_t)data_NEVER_USE & (uintptr_t)3;
        data_NEVER_USE = (uintptr_t)newData | flags;
    }

    bool hasCustomRR() const {
#if CLASS_FAST_FLAGS_VIA_RW_DATA
        return data_NEVER_USE & (uintptr_t)1;
#else
        return data()->flags & RW_HAS_CUSTOM_RR;
#endif
    }
    void setHasCustomRR(bool inherited = false);

    bool hasCustomAWZ() const {
#if CLASS_FAST_FLAGS_VIA_RW_DATA
        return data_NEVER_USE & (uintptr_t)2;
#else
        return data()->flags & RW_HAS_CUSTOM_AWZ;
#endif
    }
    void setHasCustomAWZ(bool inherited = false);

    bool isRootClass() const {
        return superclass == NULL;
    }
    bool isRootMetaclass() const {
        return isa == this;
    }
} class_t;

and this is the struct that holds everything

anyway, these are internal implementation details and should not be relied on. so do not write code that rely on these because they may broken on next runtime update

Upvotes: 8

Daij-Djan
Daij-Djan

Reputation: 50089

dont rely on any internals of the class struct. -- you don't rely on other pals instance variables either! you look for properties

all you can safely do with the runtime is in runtime.h

for getting the superclass e.g.call class_getSuperclass

Upvotes: 1

Related Questions