I've been studying the Objective-C runtime for some years, and even hacked libobjc a little bit (both Apple's and GNUStep's), and I've been wondering about a design decision on the compilers.
Every Objective-C object is expected to have its size at least of sizeof(Class), having its first field being Class isa, as seem in struct objc_object. We see it's explictly declared in root classes like NSObject and old defunct Object. We also know that the runtime adds the pointer to the new objects when they are created by class_createInstance() (see code example below).
So, my question is: then why isn't the isa pointer automatically prepended to the class declarations? Why does it need them to declare it explictly even if that is error-prone?
Example with bad code:
#import <stdlib.h>
#import <stdio.h>
#import <objc/runtime.h>
@interface Foo {
@public
int x;
};
- (void)bar;
@end
@implementation Foo
- (void)bar {
printf("bar\n");
};
@end
int main() {
Class cls = objc_getClass("Foo");
printf("cls = %p\n", cls);
// Returns sizeof(int)
printf("cls size = %ld\n", class_getInstanceSize(cls));
Foo *obj = class_createInstance(cls, 0);
printf("obj = %p\n", obj);
// Are we saving isa?
printf("hmm: %d\n", *((Class *)obj) == cls);
// We are! This works ;)
[obj bar];
// Evil code
obj->x = 10;
// Did we do something wrong?
printf("hmm: %d\n", *((Class *)obj) == cls);
// Yeah, we did! Segfault here!
[obj aaa];
return EXIT_SUCCESS;
};