Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Objective-C Class Internals

DZone 's Guide to

Objective-C Class Internals

We take a look at the nitty gritty code you can use to get started understanding internals in the Objective-C language.

· Web Dev Zone ·
Free Resource

So Objective-C is kinda old school, but it certainly influenced the development and design of Swift, so its legacy certainly lives on. And not all new apps or applications are developed in Swift today either. There's strong reasons to keep using Objective-C, including ease of access to low-level data structures, simplicity, and control.

Objective-C is an object-oriented flavor of C, with some slightly different constructs than what we're used to (I'm looking at you Protocols). Let's take a look at how Objective-C implements Classes, one of the primary design elements of any object-oriented language.

We're looking at a simple Objective-C class:

@interface Printer : NSObject

@property NSString *str_to_print;

- (void) printMsg;
- (void) printString: (NSString*) message;

@end


@implementation Printer
- (void) printMsg {
printf("%s\n", [self.str_to_print UTF8String]);
}

- (void) printString: (NSString*) message {
self.str_to_print = message;
[self printMsg];
}

@end

Objective-C classes define a class interface followed by an implementation (which is one of the reasons that Objective-C uses Protocols as what, in other languages, are called interfaces). The class itself is then compiled into a group of structures that define the class methods and data elements.

Disassembling this program, let's take a look at class object creation. When we create an object in Objective-C, we use syntax like this: Printer *obj = [[Printer alloc] init]. This allocates a new object, via the alloc()method, and then initializes it, via the init() method. If we look into the program disassembly where the object is created, we see something like this:

mov rsi, qword [objc_cls_ref_Printer]
mov rcx, qword [0x100001200]
mov rdi, rsi
mov rsi, rcx
mov qword [rbp+var_20], rax
call imp___stubs__objc_msgSend
mov rsi, qword [0x100001208]
mov rdi, rax
call imp___stubs__objc_msgSend

Notice the two CALL instructions. The first calls  alloc(), and the second calls init(). The RSI register holds the address of the method called, while the RDI register holds the address of the calling object (in this case, the Printer class and then a Printer object). The first call is the one we're interested in - the call to alloc(). We're specifically interested as it is passing in this objc_cls_ref_Printer address:

objc_cls_ref_Printer:

0000000100001218 dq _OBJC_CLASS_$_Printer

This is actually a trampoline to the class definition:

_OBJC_CLASS_$_Printer:
0000000100001250 struct __objc_class {
    _OBJC_METACLASS_$_Printer, // metaclass
    _OBJC_CLASS_$_NSObject, // superclass
    __objc_empty_cache, // cache
    0x0, // vtable
    __objc_class_Printer_data // data
}

This is where things start to become interesting. Our class definition has an implicit Superclass, the NSObject class (remember, we didn't specify this, the compiler did it for us). We also have a cache pointer, a vtable pointer, and a pointer to something called a meta-class.

The metaclass definition looks like this:

_OBJC_METACLASS_$_Printer:
0000000100001228 struct __objc_class {
    _OBJC_METACLASS_$_NSObject, // metaclass
    _OBJC_METACLASS_$_NSObject, // superclass
    __objc_empty_cache, // cache
    0x0, // vtable
    __objc_metaclass_Printer_data // data
}

So far, we have located the class definition data structure, and some kind of meta-class definition. But what do these things mean and where are the actual class methods defined? C++ has the concept of a table as well. Are they the same? Can we have virtual methods in Objective-C too?

Well, we'll trace through more of the class data next time. We do know a couple of things though: Objective-C definitely has virtual methods, but they're not the same as what we have in C++; Objective-C supports inheritance just like C++ does, and the vtable construct is used in a similar way; the vtable is an array of function addresses, but the functions pointed to are not unique to a given class definition.

All classes in a program will use the same vtable structure, which can be used to circumvent message dispatch in specific cases. This vtable is created at program launch, and no classes depend on a specific vtable configuration. Vtable pointers can point to a parent class vtable, or if no specific function is identified for an entry in the vtable, that entry is replaced with objc_msgSend(.).

So the idea behind a vtable is similar to that in C++, in that it's used for dynamic function dispatch, but the actual use of the table is remarkably different (i.e. it's not really key to inheritance). This stems from the use of message passing rather than function call semantics in Objective-C.

We still have some outstanding questions! I mean, where did the methods go? We'll start digging into this next time.

Topics:
internals ,objective-c ,web dev ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}