ios - ¿Por qué una propiedad NSMutableArray(copia, no atómica) crea NSArrays?
objective-c (2)
Cometí un error al crear una class
TableView y accidentalmente @property
mi @property
como copy
cuando la @property
:
@property (copy, nonatomic) NSMutableArray *words;
Inicialicé la matriz "correctamente": (tenga en cuenta que este es el tercer intento, así que ignore el hecho de que no estoy usando mutableCopy y otras formas mejores de hacerlo)
NSArray *fixedWords = @[@"Eeny", @"Meeny", @"Miny", @"Moe", @"Catch", @"A", @"Tiger", @"By", @"His", @"Toe"];
NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords];
self.words = mutWords;
Sin embargo, cuando más tarde llegué a reordenar la matriz, se bloqueó en la línea removeObjectAtIndex:
id object = [self.words objectAtIndex:fromIndexPath.row];
NSUInteger from = fromIndexPath.row;
NSUInteger to = toIndexPath.row;
[self.words removeObjectAtIndex:from];
Con el mensaje de error
unrecognized selector sent to instance
Tomó mucho tiempo de investigación para descubrir que esto se debe a que la copia significa que la asignación de NSMutableArray da lugar a la creación de una NSArray estándar (no mutable). ¿Alguien puede explicar por qué este es el comportamiento correcto?
-la copia, implementada por clases de cacao mutables, siempre devuelve sus equivalentes inmutables . Por lo tanto, cuando se envía una copia de NSMutableArray, devuelve una NSArray que contiene los mismos objetos.
Debido a que las words
tienen la copy
calificador de memoria, esta línea:
NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords];
self.words = mutWords;
Se expande a:
NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords];
self.words = [mutWords copy];
Dado que NSMutableArray es una subclase de NSArray, el compilador no se queja, y ahora tiene una bomba de tiempo en sus manos porque NSArray no reconoce sus métodos de subclases mutables (porque no puede mutar su contenido).
Las propiedades no son mágicas, son solo taquigrafía. Declarar una @property
en su objeto le dice al compilador que cree una variable de instancia de respaldo y métodos de acceso para ella. El código generado real depende de los atributos que establezca en su propiedad.
Es importante recordar que establecer una propiedad usando la sintaxis de puntos también es una abreviatura. Cuando usted llama…
self.words = mutWords;
... en realidad estás invocando el método de acceso generado detrás de escena, como esto:
[self setWords:mutWords];
Desde que especificó el atributo de copy
en su propiedad, le ha dicho al compilador que genere ese método -setWords:
accessor con código que se parece a esto:
- (void)setWords:(NSMutableArray *)words
{
_words = [words copy];
}
Sabiendo todo eso, puede ver lo que está sucediendo: el método de establecimiento generado invocará -copy
en el argumento de entrada, y asignará el resultado a la variable de instancia de respaldo. Debido a que el método -copy
siempre se implementa para devolver un objeto no mutable (al realizar [aMutableString copy]
se devolverá un NSString, y así sucesivamente) al establecer esa propiedad siempre se almacenará una copia no mutable.