web-dev-qa-db-fra.com

NSError et __autoreleasing

Quelqu'un peut-il m'expliquer le but d'avoir __autoreleasing dans l'exemple de bloc de code suivant?

- (void)execute:(NSError * __autoreleasing *)error {
    // do stuff, possibly assigning error if something went wrong
}

J'ai supprimé le __autoreleasing et tout semble toujours se compiler/fonctionner correctement. J'ai commencé à utiliser obj-c post ARC, donc je n'ai jamais vraiment appris/compris tous ces machins à double soulignement. J'ai lu le ARC transition guide , mais je ne comprends pas bien leur exemple NSError.

37
chinabuffet

Considérez comment ARC fonctionne avec variables - chaque variable de référence a un mode (implicite ou explicite): fort, faible, etc. Ce mode permet à ARC de savoir comment gérer les lectures et les écritures dans cette variable; par exemple. pour une strong lecture de variable ne nécessite aucune action supplémentaire tandis que l'écriture nécessite de libérer la référence existante dans la variable avant qu'elle ne soit remplacée par la nouvelle. ARC a besoin de connaître le mode de n'importe quelle variable pour fonctionner.

Considérons maintenant les variables qui elles-mêmes sont passées par référence, par exemple pour votre execute vous aurez un appel du type:

NSError *myError = nil;
...
[someObject execute:&myError]; // pass the variable itself by reference, not the variables value

et le corps de execute contiendra une affectation du type:

- (void)execute:(NSError * __autoreleasing *)error
{
   ...
   if (error != NULL)
      *error = [NSError ...]; // assign indirectly via the reference to a variable
   ...
}

Maintenant, pour cette affectation indirecte, ARC doit connaître le mode de la variable référencée afin de savoir lire et écrire. C'est ce que le __autoreleasing est dans la déclaration, il indique à ARC qu'il a été passé une référence à une variable dont le mode est autoreleasing, et qui indique à ARC comment lire et écrire le contenu de la variable. Retirer le __autoreleasing et un mode par défaut sera supposé, et dans ce cas, je suggère qu'être explicite est certainement bon.

Le mode autoreleasing signifie que la variable contient une référence qui n'appartient pas, les lectures devraient conserver si nécessaire et les écritures peuvent simplement écrire. Il est utilisé principalement pour les variables passées par référence.

Vous remarquerez peut-être que dans l'exemple ci-dessus la variable myError a le mode strong (implicitement) et pourtant elle est passée par référence comme autoreleasing - le compilateur gère cela automatiquement en introduisant une variable de libération automatique temporaire, en copiant sans en conservant la référence actuelle dans myError, et en passant le temporaire par référence comme argument à execute:. Après le retour de l'appel, le compilateur effectue une affectation normale du temporaire vers myError, ce qui entraîne la libération de toute ancienne référence et la conservation de la référence renvoyée.

Pour plus de détails, voir Apple's Transitioning to ARC Release Notes

Suivi des commentaires

Q: est __autoreleasing implicitement défini?

R: Eh bien le document d'Apple n'est pas spécifique, mais la documentation Clang dit qu'il est implicite pour les paramètres indirects. Comme ci-dessus, je recommanderais d'être explicite, la clarté est une bonne chose ™.

Q: Le placement est-il important?

R: Oui, et non ... Il s'agit d'une déclaration C, le truc des questions du quiz ("Que déclare ce qui suit ..."). Le qualificatif doit se trouver entre les deux astérisques car il s'agit d'un pointeur vers un pointeur (variable de type) autorébloquant un objet, mais Apple indique que le compilateur est "pardonner" sans être précis de ce qu'il pardonne. Jouez prudemment, mettez-le au bon endroit.

Q: Ne devriez-vous pas tester que error est NULL avant de faire l'affectation indirecte?

R: Bien sûr, vous devriez, quelque part avant de faire l'indirection. Le code affiché n'est qu'un aperçu et ces détails ont été élidés et couverts par le ... ’S. Cependant, comme cela a été soulevé plusieurs fois au fil des ans, j'ai peut-être trop élidé, un if approprié a été ajouté.

74
CRD