objective c - ¿En qué situaciones tenemos que escribir el calificador__autoreleasing ownership bajo ARC?
objective-c ios (3)
Estoy tratando de completar el rompecabezas.
__strong
es el valor predeterminado para todos los punteros de objetos retenibles de Objective-C como NSObject, NSString, etc. Es una referencia sólida. ARC lo equilibra con una -release
al final del alcance.
__unsafe_unretained
es igual a la antigua. Se usa para un puntero débil sin retener el objeto retenible.
__weak
es como __unsafe_unretained
excepto que es una referencia débil de puesta a cero automática lo que significa que el puntero se establecerá en cero tan pronto como el objeto al que se hace referencia se desasigna. Esto elimina el peligro de punteros colgantes y errores EXC_BAD_ACCESS.
Pero, ¿para qué sirve __autoreleasing
? Me está costando encontrar ejemplos prácticos sobre cuándo necesito usar este calificador. Creo que es solo para funciones y métodos que esperan un puntero-puntero como:
- (BOOL)save:(NSError**);
o
NSError *error = nil;
[database save:&error];
que bajo ARC tiene que ser declarado de esta manera:
- (BOOL)save:(NSError* __autoreleasing *);
Pero esto es demasiado vago y me gustaría entender por qué . Los fragmentos de código que encuentro ubican el __autoreleasing entre las dos estrellas, lo cual me parece extraño. El tipo es NSError**
(un puntero-puntero a NSError), así que ¿por qué colocar __autoreleasing
entre las estrellas y no simplemente delante de NSError**
?
Además, puede haber otras situaciones en las que debo confiar en __autoreleasing
.
La especificación definitiva de ARC dice que
Para __autoreleasing objects, el nuevo puntero se conserva, se libera automáticamente y se almacena en lvalue usando semántica primitiva.
Entonces, por ejemplo, el código
NSError* __autoreleasing error = someError;
En realidad se convierte a
NSError* error = [[someError retain] autorelease];
... por lo que funciona cuando tienes un parámetro NSError* __autoreleasing * errorPointer
, el método llamado asignará el error a *errorPointer
y la semántica anterior se activará.
Puede usar __autoreleasing
en un contexto diferente para forzar un objeto ARC en el pool de autorrelease, pero eso no es terriblemente útil ya que ARC solo parece usar el grupo de autorrelease en el método return y ya lo maneja automáticamente.
Siguiendo la respuesta de Macmade y la pregunta de seguimiento de Proud Member en los comentarios, (también habría publicado esto como un comentario pero excede el recuento máximo de caracteres):
Esta es la razón por la cual el calificador variable de __autoreleasing se coloca entre las dos estrellas.
Para comenzar, la sintaxis correcta para declarar un apuntador de objeto con un calificador es:
NSError * __qualifier someError;
El compilador perdonará esto:
__qualifier NSError *someError;
pero no es correcto Consulte la guía de transición Apple ARC (lea la sección que comienza "Debe decorar las variables correctamente ...").
Para dirigirse a la pregunta en cuestión: un puntero doble no puede tener un calificador de gestión de memoria ARC porque un puntero que apunta a una dirección de memoria es un puntero a un tipo primitivo, no un puntero a un objeto. Sin embargo, cuando declara un puntero doble, ARC desea saber cuáles son las reglas de administración de memoria para el segundo puntero. Es por eso que las variables de doble puntero se especifican como:
SomeClass * __qualifier *someVariable;
Entonces, en el caso de un argumento de método que es un puntero NSError doble, el tipo de datos se declara como:
- (BOOL)save:(NSError* __autoreleasing *)errorPointer;
que en inglés dice "puntero a un puntero de objeto NSError uaautoreleasing".
Tienes razón. Como explica la documentación oficial:
__autoreleasing para denotar argumentos que se pasan por referencia (id *) y se vuelven a publicar automáticamente a la vuelta.
Todo esto está muy bien explicado en la guía de transición de ARC .
En su ejemplo de NSError, la declaración significa __strong
, implícitamente:
NSError * e = nil;
Se transformará en:
NSError * __strong error = nil;
Cuando llames a tu método de save
:
- ( BOOL )save: ( NSError * __autoreleasing * );
El compilador tendrá que crear una variable temporal, establecida en __autoreleasing
. Asi que:
NSError * error = nil;
[ database save: &error ];
Se transformará en:
NSError * __strong error = nil;
NSError * __autoreleasing tmpError = error;
[ database save: &tmpError ];
error = tmpError;
Puede evitar esto declarando el objeto de error como __autoreleasing
, directamente.