Ho studiato il runtime Objective-C per alcuni anni, e ho persino hackerato un po 'di libobjc (sia di Apple che di GNUStep), e mi sono chiesto una decisione di progettazione sui compilatori.
Ci si aspetta che ogni oggetto Objective-C abbia le sue dimensioni almeno di sizeof(Class)
, con il suo primo campo che è Class isa
, come sembra in struct objc_object
. Vediamo che è dichiarato esplicitamente nelle classi radice come NSObject
e vecchio defunct Object
. Sappiamo anche che il runtime aggiunge il puntatore ai nuovi oggetti quando vengono creati da class_createInstance()
(vedi esempio di codice sotto).
Quindi la mia domanda è: allora perché il puntatore isa
non è automaticamente anteposto alle dichiarazioni di classe? Perché ha bisogno che lo dichiarino esplicitamente anche se questo è soggetto a errori?
Esempio con codice errato:
#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;
};