que objective guide framework developer iphone objective-c cocoa-touch copy

iphone - objective - swift ios documentation



NSMutableString como retener/copiar (3)

(nota: la respuesta específica de NSMutableString es un camino hacia abajo)

Tengo un número de NSMutableString en mi aplicación (casi 10-11); todo definido como ivar / propiedad

@property (nonatomic, retain) NSMutableString *str1; Leí en alguna parte que es mejor usar "copiar" para cadenas. ¿Es eso cierto?

sí, la copia de NSString (no hablamos de NSMutableString todavía) debe ser la opción predeterminada. La razón (como lo indican otros carteles) es que la implementación de una instancia inmutable puede simplemente retain en su implementación de copyWithZone: Además, la copy le da el comportamiento esperado. en otras palabras, no hay un uso para retener una cadena si de hecho es inmutable. Para garantizar que está tratando con una cadena inmutable, simplemente copie en la creación y el conjunto.

En caso afirmativo, ¿puedo simplemente reemplazar retener para copiar en mi aplicación y eliminar el lanzamiento en dealloc?

copy , como retain devuelve un objeto que debe liberar explícitamente (por ejemplo, en dealloc) en entornos recolectados sin basura. Las cuerdas inmutables todavía se cuentan como referencia. Hay excepciones a esto, en particular:

  • Los literales CF / NS-String existen para la duración del programa

  • puede utilizar las API de CF para crear cadenas que nunca se liberan, o se administran más allá del alcance de los mecanismos tradicionales de retención / liberación

  • Las subclases de tipo NS podrían implementar otro esquema (aunque las consideraciones habituales de esto son erróneas)

¿Necesito considerar algunas otras cosas también?

Ahora llegamos a los detalles de tipo NSMutable. La implementación de la sintaxis de propiedad invoca a copy . como sabemos, -[NSMutableString copy] devuelve una NSString inmutable. Este es un problema que está a punto de ocurrir para una implementación ingenua, ya que su NSMutableString ivar ahora es una NSString, que utiliza la implementación del configurador sintetizado por defecto.

@property (nonatomic, retain) NSMutableString *str1;` // or @property (nonatomic, copy) NSMutableString *str1;`

al declarar:

@property (nonatomic, retain) NSMutableString *str1;`

puede utilizar la implementación sintetizada por defecto.

pero hay un giro al declarar:

@property (nonatomic, copy) NSMutableString *str1;`

No debe utilizar la implementación sintetizada por defecto. en su lugar, debe escribir su propio :

- (void)setStr1:(NSMutableString *)arg { /* locking/observing/undo/etc omitted */ NSMutableString * copy = [arg mutableCopy]; NSMutableString * prev = str1; str1 = copy; [prev release]; } - (NSMutableString *)str1 { /* locking/observing/etc omitted */ /* don''t return something the clients thinks they may possibly modify, and don''t return something you may modify behind their back */ return [[str1 mutableCopy] autorelease]; // -- or??? return [[str1 retain] autorelease]; }

hmm ... eso no es muy claro, y no es muy considerado para los clientes. también implica un montón de cosas innecesarias e introduce una sobrecarga adicional de copia. vamos a reevaluar esto

Consideraciones / problemas adicionales en este punto:

  • el configurador predeterminado requiere que el cliente realice una copia mutable de la cadena, si solo tiene una cadena inmutable. usted se convierte inmediatamente en una copia única por usted, en el configurador, ya que no puede esperar que el cliente cree una copia mutable única solo para usted; además, ese es el deber de la implementación de la clase. por lo que NSMutableString es un mal argumento para el establecedor. NSString es el establecedor apropiado, a menos que esté reteniendo la cadena.

  • NSMutableString también es un gestor ambiguo: vea la implementación. ¿Copia + autorelease o retiene + autorelease? No hay una expectativa estándar para la forma general. Además, los clientes pueden depender de un comportamiento específico. Esto realmente es un detalle de implementación de la clase, a menos que se trate de objetos estrechamente acoplados. Los objetos estrechamente acoplados son aquellos que son básicamente co-dependientes y generalmente no reutilizables en otros contextos. ese concepto está bien, puedes estar usando una clase privada por varias razones. en cualquier caso, la semántica de copia / escritura para clientes externos y subclases no está (implícitamente) clara, y la implementación es peligrosa.

  • compartir un NSMutableString es generalmente un mal diseño. NSMutableString no es seguro para subprocesos. No tiene sentido que los clientes accedan y muten la cadena, es peligroso. Cualquier diseño que comparta objetos que no sean seguros para subprocesos debe considerarse con cuidado. compartir en este caso también se extiende al uso de subclases (así que @private en @private , siempre que sea posible)

  • retener también es generalmente un mal diseño: los clientes no sabrán cuándo la cadena se ha modificado (o se está modificando) externamente, a menos que agregue un código externo equivalente para admitirlo.

  • Dependiendo de cómo se use la interfaz, esto también puede introducir una gran cantidad de copias innecesarias. abucheo.

ok, ahora estamos bastante convencidos de que nuestra "mejora" sigue siendo una mala idea en la mayoría de los casos =) ¿cómo podemos mejorar el diseño?

Usted debe, además de los casos excepcionales de objetos fuertemente acoplados, declarar / implementar su propiedad de la siguiente manera:

@interface MONThing : NSObject { @private NSMutableString * str1; } /* note: passes/returns NSString */ @property (nonatomic, copy) NSString * str1; @end @implementation MONThing // no need for clients to create a mutableCopy - (void)setStr1:(NSString *)arg { /* locking/observing/undo/etc omitted */ NSMutableString * copy = [arg mutableCopy]; NSMutableString * prev = str1; str1 = copy; [prev release]; } // the result is clearly defined. return a copy for // thread-safety, expected behavior, and to minimize // further copies when handling the result. - (NSString *)str1 { /* locking/observing/etc omitted */ /* don''t return something the clients thinks they may possibly modify, and don''t return something you may modify behind their back */ return [[str1 copy] autorelease]; } @end

su interfaz ahora está mejorada, es más correcta, requiere menos documentación y funciona mejor.

También puede eliminar accesores visibles públicamente si no los necesita.

Podemos vivir con esa interfaz, donde se requiera.

¡Pero hay más! Como resultado, la interfaz es necesaria con menos frecuencia de lo que mucha gente piensa. En la mayoría de los casos, podemos evitar muchos de los problemas simplemente evitando el uso de NSMutableString como un ivar siempre que sea posible.

como se mencionó anteriormente, NSMutableString no es seguro para subprocesos, es más fácil y más eficiente en muchos casos usar un NSString ivar (copiado) cuando su cadena es pequeña o no cambia con frecuencia. Al agregar la cadena mutable a la interfaz (como se muestra arriba), deberá garantizar la seguridad de subprocesos manualmente. en muchos casos, es óptimo hacer algo en este sentido:

@interface MONThing : NSObject { NSString * str1; } @property (nonatomic, copy) NSString * str1; @end @implementation MONThing @synthesize str1; - (void)updateTimeElapsed:(NSTimeInterval)seconds { NSMutableString * s = [NSMutableString stringWithFormat:@"%f seconds", seconds]; /* ...some mutations... */ self.str1 = s; } @end

por supuesto, habrá algunas excepciones a esto: donde necesitará un ivar mutable, pero es mejor usar ivars inmutables cuando sea posible, y en caso de duda, en lugar de introducir una tonelada de complejidades de enhebrado. en la mayoría de los casos, puede eliminar el uso de cadenas mutables de las interfaces y construir las cadenas cuando sea necesario (como se demostró anteriormente).

Las colecciones (NSMutableArray, NSMutableDictionary, NSMutableSet, etc), por otro lado, se requieren más a menudo en las implementaciones, y son más complejas en general.

¡buena suerte!

Tengo un número de NSMutableString en mi aplicación (casi 10-11); todo definido como ivar / propiedad

@property (nonatomic, retain) NSMutableString *str1;

Leí en alguna parte que es mejor usar "copiar" para cadenas. ¿Es eso cierto? En caso afirmativo, ¿puedo simplemente reemplazar retener para copiar en mi aplicación y eliminar el lanzamiento en dealloc?

¿Necesito considerar algunas otras cosas también?

Además, ¿es normal tener 10-11 NSMutableString''s en 1 aplicación ... quiero decir desde la perspectiva del uso de la memoria? También tengo 4-5 NSMutableDictionary también en mi aplicación. Por favor, hágamelo saber si está bien.


Para copia de cadena mutable y retener tienen un resultado diferente. Si hace una copia de str1 a str2, cualquier cambio en str1 no se reflejará en str2 y cualquier cambio en str2 no se reflejará en str1, ya que son objetos separados. Pero si str1 se mantiene en str2, ambos se refieren al mismo objeto de cadena mutable y el cambio a través de str1 se reflejará en str2. Pero NSString no es mutable. Por lo tanto, para una cadena simple (es decir, no una cadena mutable), la copia solo aumenta el recuento de referencias, lo que significa que se trata internamente como retener (no estoy 100% seguro de tratar la copia como retener para objetos no mutables, pero vi esto En una pregunta diferente. En Google, editaré si puedo encontrar el enlace.

Y si usas copia, todavía necesitas liberar eso en dealloc.

Tener 10 - 11 cadenas de tamaño normal no debería tener ningún efecto negativo en el uso de la memoria. Una aplicación típica puede contener muchas más cadenas. Por tamaño normal quiero decir que no estás agregando 1 millón de caracteres en una cadena. Ese será un problema serio. La memoria tomada por una cadena aumentará a medida que aumenta la longitud de la cadena. Lo mismo ocurre con el diccionario. La memoria tomada por el diccionario dependerá de lo que esté almacenando en el diccionario. El diccionario en sí no tiene mucha sobrecarga. Siempre puede verificar los usos de memoria de su aplicación usando el instrumento.


Si bien la respuesta de Taskinoor es correcta, me gustaría agregar un poco más de explicación.

La recomendación es usar copiar para las clases que forman parte de un grupo de clases que tienen pares mutables / inmutables; como NSString / NSMutableString NSArray / NSMutableArray NSDictionary / NSMutableDictionary NSSet / NSMutableSet

La razón de esto es que es posible tener una propiedad que se declara como un tipo inmutable (como NSString) y pasarle un tipo mutable (como NSMutableString). En cuyo caso es posible cambiar la propiedad desde fuera de la clase como lo describe Taskinoor.

Se recomienda usar la copy , porque se comporta sensiblemente con los grupos de clases. el envío de una copy a una clase mutable devuelve una copia inmutable del objeto (es decir, el envío de un mensaje de copy a un NSMutableString devuelve un NSString ). Sin embargo, enviar una copy a una contraparte inmutable es equivalente a enviarle un mensaje de retain .