Ausgabe
Ich starre gerade darauf, mit Objective C herumzuspielen (Spielzeug-iPhone-Apps zu schreiben) und bin neugierig auf den zugrunde liegenden Mechanismus, der zum Versenden von Nachrichten verwendet wird. Ich habe ein gutes Verständnis dafür, wie virtuelle Funktionen in C++ im Allgemeinen implementiert werden und welche Kosten relativ zu einem statischen oder nicht virtuellen Methodenaufruf anfallen, aber ich habe keinen Hintergrund mit Obj-C, um zu wissen, wie Nachrichten gesendet werden. Beim Stöbern fand ich diesen losen Benchmark und er erwähnt, dass IMP-gecachte Nachrichten schneller sind als virtuelle Funktionsaufrufe, die wiederum schneller sind als das Senden einer Standardnachricht.
Ich versuche nicht, irgendetwas zu optimieren, sondern nur ein tieferes Verständnis dafür zu bekommen, wie genau die Nachrichten versendet werden.
- Wie werden Obj-C-Nachrichten versendet?
- Wie werden Instanzmethodenzeiger zwischengespeichert und können Sie (im Allgemeinen) durch Lesen des Codes feststellen, ob eine Nachricht zwischengespeichert wird?
- Sind Klassenmethoden im Wesentlichen dasselbe wie eine C-Funktion (oder eine statische Klassenmethode in C++), oder steckt mehr dahinter?
Ich weiß, dass einige dieser Fragen “implementierungsabhängig” sein können, aber es gibt nur eine Implementierung, die wirklich zählt.
Lösung
Wie werden Obj-C-Nachrichten versendet?
Objective-C-Nachrichten werden mithilfe der Laufzeitfunktion versendet objc_msgSend()
. Wie in der Apple-Dokumentation gezeigt , benötigt die Funktion mindestens 2 Argumente:
- Das empfangende Objekt
- Der Selektor der Nachricht
- [Eine variable Liste von Argumenten für die gesendete Nachricht.]
Instanzen einer Klasse haben einen isa
Zeiger, der ein Zeiger auf ihr Klassenobjekt ist. Die Methodenselektoren in jedem Objekt werden in einer “Tabelle” im Klassenobjekt gespeichert, und die objc_msgSend()
Funktion folgt dem isa
Zeiger auf das Klassenobjekt, um diese Tabelle zu finden, und prüft, ob die Methode in der Tabelle für die Klasse enthalten ist. Wenn es sie nicht finden kann, sucht es in der Tabelle der Oberklasse der Klasse nach der Methode. Wenn es nicht gefunden wird, fährt es im Objektbaum fort, bis es entweder die Methode findet oder zum Stammobjekt gelangt ( NSObject
). An diesem Punkt wird eine Ausnahme ausgelöst.
Wie werden Instanzmethodenzeiger zwischengespeichert und können Sie (im Allgemeinen) durch Lesen des Codes feststellen, ob eine Nachricht zwischengespeichert wird?
Aus dem Objective-C-Laufzeithandbuch von Apple zu Messaging :
Um den Messaging-Prozess zu beschleunigen, speichert das Laufzeitsystem die Selektoren und Adressen von Methoden, wenn sie verwendet werden. Für jede Klasse gibt es einen separaten Cache, der sowohl Selektoren für geerbte Methoden als auch für in der Klasse definierte Methoden enthalten kann. Vor dem Durchsuchen der Dispatch-Tabellen überprüft die Messaging-Routine zuerst den Cache der Klasse des empfangenden Objekts (auf der Grundlage der Theorie, dass eine Methode, die einmal verwendet wurde, wahrscheinlich erneut verwendet werden kann). Wenn sich der Methodenselektor im Cache befindet, ist das Messaging nur geringfügig langsamer als ein Funktionsaufruf. Sobald ein Programm lange genug läuft, um seine Caches „aufzuwärmen“, finden fast alle Nachrichten, die es sendet, eine gecachte Methode. Caches wachsen dynamisch, um neue Nachrichten aufzunehmen, während das Programm ausgeführt wird.
Wie bereits erwähnt, beginnt das Caching, sobald das Programm ausgeführt wird, und nachdem das Programm lange genug ausgeführt wurde, werden die meisten Methodenaufrufe über die zwischengespeicherte Methode ausgeführt. Wie es auch heißt, erfolgt das Caching, wenn die Methoden verwendet werden, sodass eine Nachricht nur zwischengespeichert wird, wenn sie verwendet wird.
Sind Klassenmethoden im Wesentlichen dasselbe wie eine C-Funktion (oder eine statische Klassenmethode in C++), oder steckt mehr dahinter?
Klassenobjekte handhaben das Versenden von Methoden auf ähnliche Weise wie Instanzen von Klassen. Jedes Klassenobjekt hat ein Objekt, das seine eigenen Klassenmethoden in einem Objekt namens a speichert metaclass
. Das Klassenobjekt hat seinen eigenen isa
Zeiger auf sein Metaklassenobjekt, das wiederum Super-Metaklassenobjekte hat, von denen es Klassenobjekte erben kann. Der Methodenversand an Klassenmethoden ist wie folgt:
- Das Dispatch-System folgt dem Zeiger des Klassenobjekts
isa
auf das Metaklassenobjekt - Die Methodentabelle des Metaklassenobjekts wird nach der Klassenmethode durchsucht.
- Wenn es nicht gefunden wird, fährt die Suche mit der Oberklasse des Metaklassenobjekts fort, wo die Suche fortgesetzt wird.
- Dieser Prozess wird wiederholt, bis entweder die Methode gefunden wird oder bis sie die Stammmetaklasse erreicht und eine Ausnahme ausgelöst wird.
Beantwortet von – Alex Rozanski
Antwort geprüft von – Mildred Charles (FixError Admin)