In welchen Situationen müssen wir das __autoreleasing-Eigentumsqualifikationsmerkmal unter ARC schreiben?

118

Ich versuche das Rätsel zu lösen.

__strongist die Standardeinstellung für alle Objektiv-C-Objektzeiger wie NSObject, NSString usw. Es ist eine starke Referenz. ARC gleicht es mit einem -releaseam Ende des Bereichs aus.

__unsafe_unretainedentspricht dem alten Weg. Es wird für einen schwachen Zeiger verwendet, ohne das beibehaltene Objekt beizubehalten.

__weakist wie mit der __unsafe_unretainedAusnahme, dass es sich um eine schwache Referenz mit automatischer Nullung handelt, was bedeutet, dass der Zeiger auf Null gesetzt wird, sobald die Zuordnung des referenzierten Objekts aufgehoben wird. Dadurch wird die Gefahr von baumelnden Zeigern und EXC_BAD_ACCESS-Fehlern ausgeschlossen.

Aber wofür genau ist __autoreleasinggut? Es fällt mir schwer, praktische Beispiele zu finden, wann ich dieses Qualifikationsmerkmal verwenden muss. Ich glaube, es ist nur für Funktionen und Methoden, die einen Zeiger-Zeiger erwarten, wie:

- (BOOL)save:(NSError**);

oder

NSError *error = nil;
[database save:&error];

was unter ARC folgendermaßen deklariert werden muss:

- (BOOL)save:(NSError* __autoreleasing *);

Aber das ist zu vage und ich würde gerne verstehen, warum . Die Code-Schnipsel, die ich finde, platzieren das __autoreleasing zwischen den beiden Sternen, was für mich seltsam aussieht. Der Typ ist NSError**(ein Zeiger-Zeiger auf NSError). Warum also __autoreleasingzwischen den Sternen und nicht einfach vor NSError**?

Es kann auch andere Situationen geben, auf die ich mich verlassen muss __autoreleasing.

Stolzes Mitglied
quelle
1
Ich habe dieselbe Frage und die folgenden Antworten sind nicht ganz überzeugend. Warum bietet das System beispielsweise keine Schnittstellen an, die NSError ** -Argumente verwenden, die mit dem __autoreleasing-Dekorateur wie Ihnen deklariert wurden, und die Transitioning to Arc-Versionshinweise sagen dies sollte sein? zB eine der vielen dieser Routinen in NSFileManager.h ??
Papa

Antworten:

67

Du hast recht. Wie die offizielle Dokumentation erklärt:

__autoreleasing, um Argumente zu kennzeichnen, die als Referenz (id *) übergeben und bei der Rückgabe automatisch freigegeben werden.

All dies wird im ARC-Übergangsleitfaden sehr gut erklärt .

In Ihrem NSError-Beispiel bedeutet die Deklaration __strongimplizit:

NSError * e = nil;

Wird umgewandelt in:

NSError * __strong error = nil;

Wenn Sie Ihre saveMethode aufrufen :

- ( BOOL )save: ( NSError * __autoreleasing * );

Der Compiler muss dann eine temporäre Variable erstellen, die auf festgelegt ist __autoreleasing. So:

NSError * error = nil;
[ database save: &error ];

Wird umgewandelt in:

NSError * __strong error = nil;
NSError * __autoreleasing tmpError = error;
[ database save: &tmpError ];
error = tmpError;

Sie können dies vermeiden, indem Sie das Fehlerobjekt __autoreleasingdirekt als deklarieren .

Macmade
quelle
3
Nein, __autoreleasingwird nur für Argumente verwendet, die als Referenz übergeben werden. Dies ist ein Sonderfall, da Sie einen Zeiger auf den Zeiger eines Objekts haben. Dies ist bei Convenience-Konstruktoren nicht der Fall, da sie nur einen Zeiger auf ein Objekt zurückgeben und von ARC automatisch verarbeitet werden.
Macmade
7
Warum befindet sich das Qualifikationsmerkmal __autoreleasing zwischen den Sternen und nicht nur vor NSError **? Das sieht für mich komisch aus, da der Typ NSError ** ist. Oder liegt es daran, dass damit angezeigt werden soll, dass der Zeiger auf NSError * als Hinweis auf ein automatisch freigegebenes Objekt qualifiziert sein muss?
Stolzes Mitglied
1
@Proud Mitglied in Bezug auf Ihren ersten Kommentar - das ist falsch (wenn ich Sie richtig verstehe) - siehe die Antwort von Glen Low unten. Das Fehlerobjekt wird erstellt und einer Autoreleasing-Variablen (der von Ihnen übergebenen) innerhalb der Speicherfunktion zugewiesen . Diese Zuweisung bewirkt, dass das Objekt zu diesem Zeitpunkt beibehalten und automatisch freigegeben wird. Die Deklaration der Funktion save verhindert, dass wir etwas anderes als eine Autoreleasing-Variable senden, da dies erforderlich ist. Deshalb erstellt der Compiler eine temporäre Variable, wenn wir es versuchen.
Colin
2
Warum scheint keine der Apple-Schnittstellen dies zu haben? zB alles in NSFileManager.h?
Papa
1
@Macmade: Zufällig habe ich festgestellt, dass Ihre Antwort bearbeitet wurde (von stackoverflow.com/users/12652/comptrol ) und ich habe den Eindruck, dass zumindest die Änderungen an Ihrem ersten Beispiel ("implizit ... in" umgewandelt werden) ...) sind falsch, weil das __strong-Qualifikationsmerkmal von der zweiten in die erste Zeile verschoben wurde. Vielleicht könnten Sie das überprüfen.
Martin R
34

Nachverfolgung der Antwort von Macmade und der Anschlussfrage des stolzen Mitglieds in den Kommentaren (hätte dies auch als Kommentar gepostet, aber es überschreitet die maximale Anzahl von Zeichen):

Aus diesem Grund wird das variable Qualifikationsmerkmal von __autoreleasing zwischen den beiden Sternen platziert.

Zum Vorwort lautet die korrekte Syntax zum Deklarieren eines Objektzeigers mit einem Qualifizierer:

NSError * __qualifier someError;

Der Compiler wird dies vergeben:

__qualifier NSError *someError;

aber es ist nicht richtig. Weitere Informationen finden Sie in der Apple ARC-Übergangsanleitung (lesen Sie den Abschnitt "Sie sollten Variablen korrekt dekorieren ...").

So beantworten Sie die vorliegende Frage: Ein Doppelzeiger kann kein ARC-Speicherverwaltungsqualifikationsmerkmal haben, da ein Zeiger, der auf eine Speicheradresse zeigt, ein Zeiger auf einen primitiven Typ und kein Zeiger auf ein Objekt ist. Wenn Sie jedoch einen Doppelzeiger deklarieren, möchte ARC wissen, welche Speicherverwaltungsregeln für den zweiten Zeiger gelten. Aus diesem Grund werden Doppelzeigervariablen wie folgt angegeben:

SomeClass * __qualifier *someVariable;

Im Fall eines Methodenarguments, das ein doppelter NSError-Zeiger ist, wird der Datentyp wie folgt deklariert:

- (BOOL)save:(NSError* __autoreleasing *)errorPointer;

was auf Englisch "Zeiger auf einen __autoreleasing NSError-Objektzeiger" sagt.

Binyamin Bauman
quelle
15

Die endgültige ARC-Spezifikation besagt dies

Bei __autoreleasing-Objekten wird der neue Zeiger beibehalten, automatisch freigegeben und unter Verwendung der primitiven Semantik im l-Wert gespeichert.

So zum Beispiel der Code

NSError* __autoreleasing error = someError;

wird tatsächlich konvertiert zu

NSError* error = [[someError retain] autorelease];

... weshalb es funktioniert, wenn Sie einen Parameter haben NSError* __autoreleasing * errorPointer, die aufgerufene Methode weist dann den Fehler zu *errorPointerund die obige Semantik wird aktiviert.

Sie könnten __autoreleasingin einem anderen Kontext ein ARC-Objekt in den Autorelease-Pool zwingen, aber das ist nicht besonders nützlich, da ARC den Autorelease-Pool nur bei der Methodenrückgabe zu verwenden scheint und dies bereits automatisch verarbeitet.

Glen Low
quelle