Warum null - / NULL-Blöcke Ursache bus-Fehler bei der Ausführung?
Begann ich mit blocks a Menge und bald gemerkt, dass der null-Blöcke Ursache bus-Fehler:
typedef void (^SimpleBlock)(void);
SimpleBlock aBlock = nil;
aBlock(); //bus error
Scheint dies, gegen das übliche Verhalten von Objective-C ignoriert, dass Nachrichten, die zu null-Objekte:
NSArray *foo = nil;
NSLog(@"%i", [foo count]); //runs fine
Deshalb habe ich Rückgriff auf die üblichen null prüfen, bevor ich mit einem block:
if (aBlock != nil)
aBlock();
Oder verwenden Sie dummy-Blöcke:
aBlock = ^{};
aBlock(); //runs fine
Gibt es eine andere Möglichkeit? Gibt es einen Grund, warum null-Blöcke konnte nicht einfach ein nop?
InformationsquelleAutor zoul | 2010-11-10
Du musst angemeldet sein, um einen Kommentar abzugeben.
Möchte ich das ein wenig genauer erklären, mit einer vollständigen Antwort. Betrachten wir zuerst diesen code:
Wenn Sie dies ausführen, dann sehen Sie einen Absturz auf die
block()
Zeile, die so ähnlich aussieht, wie dieser (bei der Ausführung auf einem 32-bit-Architektur - das ist wichtig):Also warum ist das So? Gut, die
0xc
ist das wichtigste bit. Der Absturz bedeutet, dass der Prozessor hat versucht, Lesen Sie die Informationen im Speicher-Adresse0xc
. Dies ist fast sicher eine völlig falsche Sache zu tun. Es ist unwahrscheinlich, dass es etwas gibt. Warum aber nicht versuchen, dies zu Lesen Speicher? Gut, es ist aufgrund der Art, in der ein block ist tatsächlich gebaut unter der Haube.Wenn ein block definiert ist, der compiler wirklich erzeugt eine Struktur auf dem stack, in dieser form:
Der block wird dann ein Zeiger auf diese Struktur. Das vierte Mitglied,
invoke
, der diese Struktur ist interessant. Es ist eine Funktion Zeiger, zeigt auf den code, wo die block-Implementierung gehalten wird. Also der Prozessor versucht zu springen-code, wenn ein block aufgerufen wird. Beachten Sie, dass wenn Sie die Anzahl der bytes in der Struktur vor derinvoke
Mitglied werden Sie feststellen, dass es 12 dezimal oder C in hexadezimaler.So, wenn ein block aufgerufen wird, der Prozessor nimmt die Adresse des Blocks, fügt 12 und versucht zu laden, wird der Wert gehalten an, dass der Speicher-Adresse. Es versucht dann zu springen-Adresse. Aber wenn der block ist der null-dann werde es versuchen zu Lesen, die Adresse
0xc
. Dies ist eine duff-Adresse, eindeutig, und so erhalten wir die "segmentation fault".Nun der Grund, es muss ein crash wie diese eher als leise scheitern wie ein Objective-C-Nachricht ein Anruf ist wirklich eine design-Wahl. Da der compiler die Arbeit macht, zu entscheiden, wie aufrufen, den block, es hätte zu injizieren null-checking code überall ein block aufgerufen wird. Das würde mehr code-Größe und führen zu schlechter Leistung. Eine weitere option wäre die Nutzung einer Trampolin der null-Kontrolle. Aber dieses würde auch entstehen Leistungseinbußen. Objective-C-Nachrichten schon mal über ein Trampolin, da Sie benötigen, um sich die Methode, die eigentlich aufgerufen werden. Die Laufzeitumgebung ermöglicht es für lazy-injection-Methoden und ändern von methodenimplementierungen, so ist es schon über ein Trampolin sowieso. Die zusätzliche Strafe zu tun, die null-Kontrolle nicht signifikant in diesem Fall.
Ich hoffe, das hilft ein wenig, um zu erklären, die Begründung.
Weitere Informationen finden Sie unter " meine blog Beiträge.
InformationsquelleAutor mattjgalloway
Matt Galloway ' s Antwort ist perfekt! Große Lesen!
Ich möchte nur hinzufügen, dass es gibt einige Möglichkeiten, das Leben leichter zu machen. Definieren Sie ein makro wie dieses:
Kann es 0 – n Argumente. Beispiel für die Verwendung
Wenn, Sie wollen den Rückgabewert des Blocks und Sie sind sich nicht sicher, ob der block vorhanden ist oder nicht, dann sind Sie wahrscheinlich besser dran, nur eingeben:
Diese Weise können Sie einfach definieren Sie den fallback-Wert. In meinem Beispiel ist 'null'.
Ich habe noch nie getestet,
InformationsquelleAutor hfossli
Caveat: ich bin kein Experte in Blöcken.
Blöcke sind objective-c-Objekte aber Berufung ein block ist nicht eine Nachricht, obwohl Sie könnte noch versuchen
[block retain]
ing einenil
block oder andere Nachrichten.Bleibt zu hoffen, dass (und die links) hilft.
Sie können in der Lage sein, um eine Kategorie hinzuzufügen, um die
__block
geben... aber ich bin mir nicht sicher.#define nilBlock ^{}
kann auch Ihr Leben leichter machen.Ich dachte über die
nilBlock
Ansatz, leider ist die Eingabe in die Quere kommt – das erstellen einer separaten null-Wert für jeden block ist nicht viel Spaß.Ich habe keine Ahnung, ob Sie können eine Unterklasse Blöcke, aber das hinzufügen einer Nachricht
[block call]
die interne Prüfung helfen könnte. Nicht sicher, wie weit die Blöcke sind zu ObjC von Objekten.Ich in der Regel einfach nur blockieren ? sperren() : null; das ist knapp genug für mich und ist transparent in dem, was Ihr tut...
InformationsquelleAutor Stephen Furlani
Dies ist mein einfaches schönste Lösung... Vielleicht es ist möglich, schreiben Sie eine universal-run-Funktion mit den c-var-args-aber ich weiß nicht, wie zu schreiben, dass.
wir sind der Rückgabe etwas in diesen Methoden ? Nein. Also kaum ...
InformationsquelleAutor Renetik