web-dev-qa-db-fra.com

Dans quelles situations devons-nous écrire le qualificatif de propriété __autoreleasing sous ARC?

J'essaie de terminer le puzzle.

__strong est la valeur par défaut pour tous les pointeurs d'objet retenus Objective-C comme NSObject, NSString, etc. C'est une référence forte. ARC équilibre avec un -release à la fin de la portée.

__unsafe_unretained est égal à l'ancienne méthode. Il est utilisé pour un pointeur faible sans conserver l'objet pouvant être retenu.

__weak est comme __unsafe_unretained sauf qu'il s'agit d'une référence faible à mise à zéro automatique, ce qui signifie que le pointeur sera défini sur zéro dès que l'objet référencé sera désalloué. Cela élimine le risque de pointeurs pendants et d'erreurs EXC_BAD_ACCESS.

Mais quel est exactement __autoreleasing bon pour? J'ai du mal à trouver des exemples pratiques sur le moment où j'ai besoin d'utiliser ce qualificatif. Je crois que ce n'est que pour les fonctions et les méthodes qui attendent un pointeur-pointeur tel que:

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

ou

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

qui en vertu de l'ARC doit être déclaré de cette façon:

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

Mais c'est trop vague et j'aimerais bien comprendre pourquoi. Les extraits de code que je trouve placent le __autoreleasing entre les deux étoiles, ce qui me semble bizarre. Le type est NSError** (un pointeur sur NSError), alors pourquoi placer __autoreleasing entre les étoiles et pas simplement devant NSError**?

En outre, il peut y avoir d'autres situations dans lesquelles je dois compter sur __autoreleasing.

114
Proud Member

Tu as raison. Comme l'explique la documentation officielle:

__autoreleasing pour indiquer les arguments passés par référence (id *) et libérés automatiquement au retour.

Tout cela est très bien expliqué dans le ARC transition guide .

Dans votre exemple NSError, la déclaration signifie __strong, implicitement:

NSError * e = nil;

Sera transformé en:

NSError * __strong error = nil;

Lorsque vous appelez votre méthode save:

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

Le compilateur devra alors créer une variable temporaire, définie sur __autoreleasing. Alors:

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

Sera transformé en:

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

Vous pouvez éviter cela en déclarant l'objet d'erreur comme __autoreleasing, directement.

67
Macmade

Suivi de la réponse de Macmade et de la question de suivi de Proud Member dans les commentaires (aurait également posté cela en tant que commentaire mais il dépasse le nombre maximal de caractères):

Voici pourquoi le qualificatif variable de __autoreleasing est placé entre les deux étoiles.

Pour préfacer, la syntaxe correcte pour déclarer un pointeur d'objet avec un qualificatif est:

NSError * __qualifier someError;

Le compilateur pardonnera ceci:

__qualifier NSError *someError;

mais ce n'est pas correct. Voir le Apple guide de transition ARC (lire la section qui commence "Vous devez décorer les variables correctement ...").

Pour répondre à la question posée: Un double pointeur ne peut pas avoir de qualificateur de gestion de mémoire ARC car un pointeur qui pointe vers une adresse mémoire est un pointeur vers un type primitif, pas un pointeur vers un objet. Toutefois, lorsque vous déclarez un double pointeur, ARC ne souhaite pas connaître les règles de gestion de la mémoire pour le deuxième pointeur. C'est pourquoi les variables à double pointeur sont spécifiées comme:

SomeClass * __qualifier *someVariable;

Ainsi, dans le cas d'un argument de méthode qui est un double pointeur NSError, le type de données est déclaré comme suit:

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

qui en anglais dit "pointeur vers un pointeur d'objet __autoreleasing NSError".

34
Binyamin Bauman

La spécification définitive ARC dit que

Pour les objets __autoreleasing, le nouveau pointeur est conservé, libéré automatiquement et stocké dans la valeur l à l'aide de la sémantique primitive.

Ainsi, par exemple, le code

NSError* __autoreleasing error = someError;

est en fait converti en

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

... c'est pourquoi cela fonctionne quand vous avez un paramètre NSError* __autoreleasing * errorPointer, la méthode appelée affectera ensuite l'erreur à *errorPointer et la sémantique ci-dessus se déclenchera.

Vous pouvez utiliser __autoreleasing dans un contexte différent pour forcer un objet ARC dans le pool d'autorelease, mais ce n'est pas très utile car ARC ne semble utiliser le pool d'autorelease qu'au retour de la méthode et le gère déjà automatiquement.

15
Glen Low