In Cocoa, how is the class type defined?


From "objc.h":

typedef struct objc_class *Class;

but in "runtime.h":

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;

/* Use `Class` instead of `struct objc_class *` */

What exactly is Class?

typedef struct objc_class *Class;

^ That is a forward declaration for an objc_class pointer. It gives it the nice friendly name Class.

Now lets take a look at the objc_class struct: (Removing Objective-C 2.0 checks to shorten it up)

struct objc_class {
    Class isa;
//This is really saying
struct objc_class {
    struct objc_class *isa;

So now we have a struct that points to its own type. But why you ask?

Taken from Inside the Objective-C Runtime, Part 2

The Class pointed to by the Class' isa (the Class' Class) contains the Class' class methods in its objc_method_list. Got that? The terminology used in the runtime is that while an object's isa points to its Class, a Class's isa points to the object's "meta Class".

So what about a meta Class' isa pointer? Well, that points to the root class of the hierarchy (NSObject in most cases). (In the Foundation framework each meta class of a subclass of NSObject "isa" instance of NSObject.) And yes, by the way, NSObject's meta Class' isa points back to the same struct -- it's a circular reference so no Class' isa is ever NULL.

So according to that description when you create a class inheriting from NSObject you have an isa that points to its class type which points to its meta class which points to its root class (NSObject) which contains a circular reference to itself. This explains the reason why it requires two steps to determine if an object is an instance or a class.

Lets say you create the following class:

@interface MyClass : NSObject
@implementation MyClass

If you were able* to traverse the isa pointer you would get the following:
*You can not since the isa is protected. See Cocoa with Love post below for creating your own implementation.

Class c = myClassInstance->isa; //This would be the MyClass
c = c->isa; //This would be MyClass' meta class
c = c->isa; //This would be NSObjects meta class
c = c->isa; //This going forward would still be the NSObjects meta class

Once c == c->isa you will know you are at the root object. Remember that Objective-C is a proper superset of C, which does not natively have object-oriented constructs such as inheritance. This along with other implementation details, such as pointers to super classes which make up the full class hierarchy, allows Objective-C to provide object-oriented features. For more information on classes and meta-classes see the post on Cocoa with Love: What is a meta-class in Objective-C?