Edit AllPages

This page provides a summary of how Objective-C is implemented in C using Apple’s Objective-C runtime. Other runtimes are similar.

As a C programmer, perhaps you need the “magic” removed so you can see what is happening in Objective-C.

Objective-C adds the concepts of objects and message sending to the C language. Any message may be sent to any object. By default, there is a runtime error if the receiver of a message does not understand the message, although some receivers may suppress the error or handle the unrecognized message my passing it on to an object that does understand it.

But first, let’s propose these implementation neutral definitions: An Objective-C object is anything that can receive a message. A message is just a request for the receiver to do something using arguments that may be provided with the message. In some cases, sending a message may return a value to the sender.

Those definitions may seem vague, but it is that simple, and there are huge implications to this. Just to whet your appetite, neither the receiver nor the message needs to be known at compile time, and this is the great strength (flexibility) of Objective-C and similar languages like Smalltalk, Ruby, and Python but not like C++ or Java.

So, for a C programmer, what is an Objective-C object ? In implementation, and Objective-C object is any C structure whose first element is a variable of type Class. In objc.h on your hard drive, you will find the C type Class is declared as

typedef struct objc_class *Class;

Another fun C typedef is the id type used to store a pointer to any Objective-C object:

typedef struct objc_object { Class isa; } *id;

So, any variable of type id is just a pointer to any structure that contains as its first element a pointer to a variable of type Class.

Now that you know exactly what an Objective-C object is from a C programmer’s point of view, what is a message ?

Apple’s Objective-C implementation provides a couple of C functions:

id objc_msgSend(id self, SEL op, …); id objc_msgSendSuper(struct objc_super *super, SEL op, …);

The SEL type is the C type that identifies what message is being sent. In objc_msgSend(id self, SEL op, ...), self is the receiver of the message, SEL identifies the message being sent, and any number of arguments may follow.

objc_msgSendSuper() is similar to objc_msgSend(). The only relevant difference between the two is where the function looks first to find an IMP to call in response to the message. What’s an IMP you ask ?

typedef id (*IMP)(id, SEL, …);

IMP is a C typedef for a pointer to a function that takes the same arguments as objc_msgSend() and returns the same type. The code that is executed in response to receipt of Objective-C messages is compiled into C functions by the Objective-C compiler. Pointers to those functions are then associated with messages using a hash table that maps SEL to IMP.

objc_msgSend() and objc_msgSendSuper() simply use the provided SEL argument to look-up the corresponding IMP and than call the IMP passing the same arguments that were passed to objc_msgSend() and objc_msgSendSuper().

That is Objective-C.

Now that you know what an object is, let’s look at some Objective-C code to see the corresponding C code.

NSArray *fileTypes = [NSArray arrayWithObject:@”td”];

NSArray * and NSOpenPanel * are both synonyms for the C type id.

The following code will produce exactly the same machine code when compiled with Objective-C as the code above:

id fileTypes = [NSArray arrayWithObject:@”td”];

So you know that fileTypes is a variable that points to a struct objc_object.

id fileTypes = [NSArray arrayWithObject:@"td"]; is a synonym for

struct objc_object *fileTypes = objc_msgSend(objc_lookUpClass(“NSArray”), sel_getUid(“arrayWithObject:”), @”td”);

For reference: In objc.h see the declaration SEL sel_getUid(const char *str);. In objc-runtime.h see the declaration id objc_lookUpClass(const char *name);.

ignoring @"td" for a moment…

you now see that id fileTypes = [NSArray arrayWithObject:@"td"]; is just a syntactically nicer way of writing

struct objc_object *fileTypes = objc_msgSend(objc_lookUpClass(“NSArray”), sel_getUid(“arrayWithObject:”), @”td”);

In reality, the Objective-C compiler can optimize things a bit to eliminate some of the function calls at run-time, but that is beside the point right now.

What is the significance of the string "NSArray" ? There is an Objective-C class named NSArray provided by the Cocoa framework. As you can guess from the C language implementation of Objective-C, objc_lookUpClass() just returns an id type value that was associated with the string"NSArray" by the Objective-C compiler. Yes, that’s right, just like the compiler generates a hash table to associate IMP with SEL, it generates a hash table to associate ids with Objective-C class names like NSArray. So, when the compiler compiled the class NSArray, the compiler generated the following things:

*the compiler generated a static C structure whose first element is of type Class. *the compiler generated a hash table entry associating a pointer to the static structure with the string "NSArray". *the compiler generated a hash table entry associating a SEL with the string "arrayWithObject:". *The compiler generated a C function containing code to be executed in response to the arrayWithObject: message selector (SEL). *the compiler generated a hash table entry associating a pointer to the C function (IMP) with the SEL previously associated with the string "arrayWithObject:".

All that stuff generated by the compiler makes id fileTypes = objc_msgSend(objc_lookUpClass("NSArray"), sel_getUid("arrayWithObject:"), @"td") work at run-time.