Lange Antwort:
die mit Ihrer letzten Frage Beginnen wir:
Da darüber hinaus respondsToSelector
ist eine Instanzmethode, warum ist es sogar möglich, es in erster Linie zu nennen?
respondsToSelector:
ist eine Instanzmethode des NSObject
Protokolls, zu der die NSObject
Klasse entspricht. Nun ist die NSObject
Klasse (und jede ihrer Unterklassen) ein Objekt und eine Instanz einer Unterklasse der Stammklasse NSObject
.
Dies wird erläutert und dargestellt in Greg Parker Artikel [objc explain]: Classes and metaclasses (Hervorhebung hinzugefügt):
Wichtiger ist die übergeordnete Klasse einer Metaklasse. Die Superklassenkette der Metaklasse entspricht der Oberklassenkette der Klasse, sodass Klassenmethoden parallel mit Instanzmethoden vererbt werden. Und die Oberklasse der Wurzelmetaklasse ist die Stammklasse , so dass jedes Klassenobjekt auf die Instanzmethoden der Stammklasse antwortet. Am Ende ist ein Klassenobjekt eine Instanz von (einer Unterklasse von) der Wurzelklasse, genau wie jedes andere Objekt.
Dies erklärt, warum Sie überhaupt respondsToSelector:
zum NSObject
Klasse senden. Der Rückgabewert ist YES
, wenn es eine Klassenmethode mit dem angegebenen Selektor gibt.
Hier ist ein weiteres Beispiel:
NSString *path = [NSString performSelector:@selector(pathWithComponents:) withObject:@[@"foo", @"bar"]];
performSelector:withObject:
ist eine Instanz-Methode von NSObject
, und Sie können diese Mitteilung an die NSString
Klasse senden. In diesem Fall ist das Ergebnis das gleiche wie
NSString *path = [NSString pathWithComponents:@[@"foo", @"bar"];
Nun zu Ihrer ersten Frage:
Warum respondsToSelector
auf NSObject mit dem Selektor „init“, obwohl läuft zurückkehren 1 selbst dann nicht läuft [NSObject init]
gibt einen Laufzeitfehler?
Mit der gleichen Argumentation wie oben, muss es möglich sein, die init
Nachricht an all Klasse zu senden, die von NSObject
abgeleitet ist.
Jetzt NSObject
hat eine Klassenmethode init
, die dokumentiert wird, um eine Ausnahme zur Laufzeit zu werfen, sehen http://opensource.apple.com/source/objc4/objc4-532.2/runtime/NSObject.mm:
// Replaced by CF (throws an NSException)
+ (id)init {
return (id)self;
}
Dies erklärt, warum
[NSObject respondsToSelector:@selector(init)] == YES
Der Kommentar in NSObject .mm gibt an, dass +init
in CoreFoundation und in der Tatüberschrieben wirdwenn die Ausnahme ausgelöst wird, der Stapel Backtrace ist
(lldb) bt
* thread #1: tid = 0x6eda, 0x01f69952 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
....
frame #8: 0x0156b9fc libobjc.A.dylib`objc_exception_throw + 323
frame #9: 0x01889931 CoreFoundation`+[NSObject(NSObject) init] + 241
* frame #10: 0x00002a51 foo`main(argc=1, argv=0xbfffee30) + 257 at main.m:25
so dass diese Methode für Corefoundation Ausnahme zuständig ist.
Ich weiß nicht, warum die init
Methode in Corefoundation außer Kraft gesetzt wird (statt eine Ausnahme direkt in NSObject
werfen) und die ersetzt Verfahren Teil der Open Source-Repository zu sein scheinen nicht http://opensource.apple.com/source/CF/CF-855.14/ zu sein (Zumindest konnte ich es dort nicht finden).
Aber ein interessanter Punkt kann sein, dass das beobachtete Verhalten zwischen OS-Versionen geändert hat. Wenn
id x = [NSObject init];
auf OS X 10.5 kompiliert wird, dann funktioniert es und gibt ein NSObject
Klassenobjekt. Dies funktioniert auch, wenn die Binärdatei unter OS X 10.5 kompiliert und verknüpft wurde und auf einer späteren Version wie als OS X 10.9 ausgeführt wird.
Dies kann auch aus dem Assembler-Code von
CoreFoundation`+[NSObject(NSObject) init]:
0x1019d8ad0: pushq %rbp
0x1019d8ad1: movq %rsp, %rbp
0x1019d8ad4: pushq %rbx
0x1019d8ad5: pushq %rax
0x1019d8ad6: movq %rdi, %rbx
0x1019d8ad9: movl $0x6, %edi
0x1019d8ade: callq 0x1018dfcc0 ; _CFExecutableLinkedOnOrAfter
0x1019d8ae3: testb %al, %al
0x1019d8ae5: jne 0x1019d8af1 ; +[NSObject(NSObject) init] + 33
0x1019d8ae7: movq %rbx, %rax
0x1019d8aea: addq $0x8, %rsp
0x1019d8aee: popq %rbx
0x1019d8aef: popq %rbp
0x1019d8af0: ret
0x1019d8af1: movq %rbx, %rdi
0x1019d8af4: callq 0x101a19cee ; symbol stub for: class_getName
0x1019d8af9: leaq 0x9fe80(%rip), %rcx ; kCFAllocatorSystemDefault
0x1019d8b00: movq (%rcx), %rdi
0x1019d8b03: leaq 0xc5a66(%rip), %rdx ; @"*** +[%s<%p> init]: cannot init a class object."
...
0x1019d8b72: callq *0x9e658(%rip) ; (void *)0x00000001016b9fc0: objc_msgSend
0x1019d8b78: movq %rax, %rdi
0x1019d8b7b: callq 0x101a19d4e ; symbol stub for: objc_exception_throw
_CFExecutableLinkedOnOrAfter()
(von http://www.opensource.apple.com/source/CF/CF-476.14/CFPriv.h) prüft, ob die Binärdatei auf OS zu sehen X 10.6 oder höher in Verbindung gebracht wurden, und löst eine Ausnahme in diesem Fall.
So init
auf das Klassenobjekt aufrufen müssen in früheren OS Versionen erlaubt worden, und Corefoundation erlaubt es immer noch in „alten Binärdateien“ für die Abwärtskompatibilität.
Sehr nette Antwort! –