2010-08-27 8 views
5

habe ich versucht, diesen Code:Was ist erforderlich, um die Root-Klasse von Objective-C zu implementieren?

// main.m 
#import <stdio.h> 

@interface Test 
+ (void)test; 
@end 
@implementation Test 
+ (void)test 
{ 
    printf("test"); 
} 
@end 

int main() 
{ 
    [Test test]; 
    return 0; 
} 

mit LLVM/Clang ohne Rahmen, es nicht mit diesem Fehler kompiliert:

Undefined symbols: 
    "_objc_msgSend", referenced from: 
     _main in main.o 
ld: symbol(s) not found 
clang: error: linker command failed with exit code 1 (use -v to see invocation) 

ich hinzugefügt So libobjc.dylib. Code kompiliert, sondern warf diese Laufzeitausnahme:

objc[13896]: Test: Does not recognize selector forward:: 
Program received signal: “EXC_BAD_INSTRUCTION”. 

#0 0x9932a4b4 in _objc_error 
#1 0x9932a4ea in __objc_error 
#2 0x993212b6 in _objc_msgForward 
#3 0x99321299 in _objc_msgForward 
#4 0x99321510 in _class_initialize 
#5 0x99328972 in prepareForMethodLookup 
#6 0x99329c17 in lookUpMethod 
#7 0x99321367 in _class_lookupMethodAndLoadCache 
#8 0x99320f13 in objc_msgSend 
#9 0x00001ee5 in start 

ich eine Implementierung realisiert für Wurzelklasse erforderlich ist, aber ich weiß nicht, was soll ich als nächstes tun. Was ist erforderlich, um eine neue Root-Klasse zu erstellen? Und gibt es dafür eine Spezifikation?

+0

ich eine wertvolle Ressource gefunden: http://code.google.com/p/ purefoundation/ Ich weiß aber immer noch nicht über Spezifikationen – Eonil

+0

Nur für spätere Referenz, Apples Open-Source-Objc-Laufzeitquelle enthält auch "NSObject" Quelle – Eonil

Antwort

6

Ich bin gerade zu dieser Frage gekommen, weil ich dieselbe "akademische" Frage hatte. Nachdem ich mich ein wenig durchgearbeitet habe, habe ich festgestellt, dass die anderen Antworten auf diese Frage nicht ganz korrekt sind.

Es ist wahr, dass auf der Apple Objective-C 2.0 Laufzeit, müssen Sie bestimmte Methoden implementieren, damit Ihr Code funktioniert.Es gibt nur eine Methode, die Sie implementieren müssen: die Klassenmethode initialize.

@interface MyBase 
+ (void)test; 
@end 
@implementation MyBase 
+ (void)initialize {} 
+ (void)test { 
// whatever 
} 
@end 

Die Laufzeit wird initialize automatisch anrufen, wenn Sie zuerst Ihre Klasse verwenden (wie in Apple's documentation erklärt). Das Implementieren dieser Methode ist der Grund für den Nachrichtenweiterleitungsfehler.

Durch das Kompilieren mit clang test.m -Wall -lobjc (oder gcc) können Sie den Klassenmethode-Test ohne Probleme aufrufen. Die Aufgabe der Objektzuordnung ist eine andere Geschichte. Zumindest benötigen Sie einen Zeiger isa auf Ihrer Basisklasse, wenn Sie Instanzvariablen verwenden. Die Laufzeitumgebung erwartet dies.

+0

Du hast recht. Es klappt! Danke, das ist ein großer Schritt für mich :) – Eonil

2

Auf der Apple-Laufzeit, sind die minimalen Spezifikationen ziemlich leicht zu erklären: Sie haben jede Methode im NSObject Protokoll zu implementieren. Siehe here. Dies ist absolut nicht trivial. Vielleicht möchten Sie ein paar zusätzliche Funktionen wie +alloc hinzufügen, um eine Instanz erstellen zu können. Es gibt nur zwei öffentliche Stammklassen in allen Frameworks von Apple: NSObject und NSProxy. In der Praxis gibt es absolut keinen Grund, eine Root-Klasse zu erstellen. Ich bin mir nicht sicher, ob es eine Dokumentation zu diesem Problem von Apple gibt.

Was Sie in der Praxis tun werden, ist von NSObject oder NSProxy zu erben und auf ihnen zu bauen. Ihr Code wird funktionieren, wenn Sie wie folgt vorgehen:

@interface Test : NSObject 
+ (void)test; 
@end 

Wie Tilo wies darauf hin, ist dies nicht der Fall auf andere Runtimes wie der GNU-Laufzeit.

+0

Sie haben Recht Max: Implementierung einer neuen Root-Klasse ist anspruchsvoll und kann interessant sein aus akademischer Sicht, aber praktisch gibt es nur sehr wenige Anwendungen – GorillaPatch

+0

In Wirklichkeit hat Apple nur 5 Wurzelklassen in allen Cocoa-Frameworks erstellt, einschließlich nsobject und nsproxy, die ich nie selbst erstellen musste. –

+0

Es war nur eine Art von Versuch, objc Programm auf Nicht-Apple-Plattform (wie FreeBSD). Ich stimme zu, es gibt keinen Grund, meine eigene Root-Klasse in Apple-Plattform zu machen :) – Eonil

1

Das obige Beispiel kompiliert gut mit gcc und GNU-Laufzeit. In Objective-C kann normalerweise jede Klasse eine Root-Klasse sein, indem man einfach keine Superklasse hat. Wenn die Apple-Laufzeit etwas anderes benötigt, ist es laufzeitspezifisch.

Zusätzlich gibt es etwas Bestimmtes mit Root-Klassen:

Alle Instanzmethoden sind auch Klassenmethoden mit derselben Implementierung (wenn nicht ausdrücklich anders implementiert). Die Ausgabe der folgenden App:

#import <stdio.h> 

@interface Test 
+ alloc; 
+ (void)test; 
- (void)test; 
- (void)otherTest; 
@end 
@implementation Test 
+ alloc 
{ 
    return (id)class_create_instance(self); 
} 

+ (void)test 
{ 
    printf("class test\n"); 
} 

- (void)test 
{ 
    printf("instance test\n"); 
} 

- (void)otherTest 
{ 
    printf("otherTest\n"); 
} 
@end 

int main() 
{ 
    id t = [Test alloc]; 
    [Test test]; 
    [t test]; 
    [Test otherTest]; 
    [t otherTest]; 
    return 0; 
} 

wäre:

class test 
instance test 
otherTest 
otherTest 

Der schwierigste Teil eine neue Stammklasse auf der Schaffung die +alloc und -dealloc aber wie in meinem Beispiel die Laufzeit (in meinem Fall gesehen die GNU-Laufzeit) kann dies tun. Aber ich weiß nicht, ob die Laufzeitzuweisung gut genug ist. Ich weiß, dass einige Stiftungen einen eigenen Zuweisungsmechanismus verwenden, um den Referenzzähler vor der Objektstruktur zu verbergen. Ich weiß nicht, ob Apple das auch macht und ob sich ihre Laufzeit schon darum kümmert.

2

In meinem System (GNUstep auf Linux + GCC) musste ich die obige Alloc-Methode durch die folgenden ersetzen, um das Beispiel zu arbeiten. Ich denke, dies ist aufgrund einer neueren obj-c-Laufzeit (Dokumentation der Laufzeit hier:. Objective-C Runtime Reference vom Mac Developer Library

+ alloc 
{ 
    return (id)class_createInstance(self, 0); 
}