If you’re an Objective-C developer you can get a lot done without understanding the C roots in Objective-C or the object model employed by Objective-C. In fact, trying to learn the object model can be intimidating and confusing because some peculiar complexities.

Even if it’s a little complex, it’s a worthy endeavor to learn the Objective-C object model because you may end up needing to interact with the Objective-C runtime someday, and understanding the object model will be very useful in that situation.

Grab a cup of tea or coffee and follow along with me while we explore the Objective-C object model.

Objects are instances of a class

Sure, this may sound obvious, but it’s worth taking a few seconds to really consider what it means when we say that an object is an instance of a class. Take the following code for example:

NSString *name =
  [NSString stringWithFormat:@"%@", @"Peter"];

This is a rather contrived example but it’s important to demonstrate some of the moving parts in the Objective-C runtime. When the code above is executed we end up with a variable called name that is an instance of the NSString class. Please consider the following:

  • Objects (instances of a class) are used to hold state.

  • An object’s state is held in its instance variables. For example, the name object above owns some memory for storing the bytes that make up the string “Peter”.

  • The name object also contains a pointer back to its class (NSString) as part of its state.

  • In Objective-C objects don’t contain any behavior, only state.

  • Classes contain behavior and do not contain any state. The behavior they hold is the methods that their instances can respond to.

  • Let’s repeat that once again for clarity. Objects are containers that store state (instance variables) and classes are containers that store behavior (methods).

To make this more concrete take a look at the following diagram:

Object Method Dispatch

The blue box represents the name variable (an instance of the NSString class). Using the diagram above let’s look at what happens if you send the length message to the name variable:

NSUInteger len = [name length];
  1. The first thing that Objective-C does is ask the name variable which class it was instantiated from. Since all the methods are held in the class, that’s where it will start looking for the length method.

  2. Since the class of name object is NSString, Objective-C goes to the NSString class and searches for the length method.

  3. If Objective-C finds the length method in the NSString class it will stop searching and execute the method that it found. Otherwise it will continue searching the inheritance hierarchy for the method.

I should note that the process I’m describing has been simplified. I’m not considering other places that methods could be stored (e.g. Objective-C categories).

Classes are really objects too

Given that a class holds methods for its instances, you might be wondering where class methods are stored. Let’s look at the code that created the name variable again:

NSString *name =
  [NSString stringWithFormat:@"%@", @"Peter"];

In the code above, the stringWithFormat: message was sent to the NSString class. Can classes receive messages, and if so where are those methods kept? In this case, the Objective-C syntax is covering something up, it’s hiding the fact that NSString is also an object.

If NSString is an object, that means that it must have been instantiated from a class that is holding its methods. This is true, but before we dig into it let’s look at some more code:

@interface Person : NSObject {}
+ (void) aClassMethod;
- (void) anInstanceMethod;

You’ve probably seen this many, many times. But what is really going on?

  • A new class is being declared, the Person class.

  • This class will be used to hold methods for instances.

  • A second class is also being created behind the scenes. Unfortunately this additional class has the same name (Person) but is referred to as the Person metaclass.

  • The Person metaclass holds the class methods for the Person class.

This can be very confusing because the same name is being used over and over again. Going back to our name variable, we can say the following:

  • The name object is an instance of the NSString class.

  • NSString is an object instantiated from the NSString metaclass.

  • If you send the length message to the name object, Objective-C will go look for that method in the NSString class.

  • If you send the stringWithFormat: message to the NSString object, Objective-C will go look for that method in the NSString metaclass.

Here’s another diagram:

Metaclass Method Dispatch

This time the blue box represents the NSString object. Here’s the creation of the name object again:

NSString *name =
  [NSString stringWithFormat:@"%@", @"Peter"];

When the stringWithFormat: message is sent to the NSString object Objective-C will:

  1. Ask the NSString object for it’s class, which is the NSString metaclass.

  2. It will then start searching the NSString metaclass for the stringWithFormat: method. If it doesn’t find the method there it will continue up the inheritance hierarchy.

Naturally, since the NSString class inherits from the NSObject class, the NSString metaclass inherits from the NSObject metaclass.

Wacky NSObject inheritance

If you’ve made it this far you can rest assured that you know enough about the Objective-C object model to know a lot more about what’s going on in your code at run time. There’s just one more thing that you may want to know: Why does it look like the NSObject metaclass inherits from the NSObject class? Because it does.

Consider the following:

  • The NSObject class contains methods for its instantiated objects.

  • The NSObject metaclass contains methods for the NSObject class (i.e. NSObject class methods).

  • The NSObject metaclass inherits from the (non-meta) NSObject class.

  • Therefore, any instance methods defined for NSObject will also be in the search path as class methods for any Objective-C class.

It’s a bit wacky, but if you look at the diagram above it should start to make sense.