2010-01-19 16 views
7

Esto produce un objeto de cadena inmutable:Cacao: ¿Pruebas para encontrar si un NSString es inmutable o mutable?

NSString* myStringA = @"A"; //CORRECTED FROM: NSMutableString* myStringA = @"A"; 

Esto produce un objeto de cadena mutable:

NSMutableString* myStringB = [NSMutableString stringWithString:@"B"]; 

Pero ambos objetos son reportados como el mismo tipo de objeto, "NSCFString":

NSLog(@"myStringA is type: %@, myStringB is type: %@", 
[myStringA class], [myStringB class]); 

Entonces, ¿qué es lo que distingue a estos objetos internamente, y cómo lo pruebo, para poder determinar fácilmente si una cadena de caracteres misteriosa es inmut capaz o mutable antes de hacer algo malvado?

+0

El código de Philippe a continuación de - if ([myStringB isKindOfClass: [NSMutableString class]]) - trabaja y resuelve el problema práctico. – StringSection

+0

Todavía tengo curiosidad sobre cómo se representa internamente la diferencia entre una cadena inmutable y mutable, y si eso se puede detectar directamente (el "tipo de objeto" real impreso con NSLog). – StringSection

+1

Corrección: Estaba equivocado, el código - if ([myStringB isKindOfClass: [NSMutableString class]]) - no está funcionando después de todo. Devuelve verdadero si la cadena es NSString o NSMutableString. Como Philippe indica a continuación (en su respuesta ahora editada con código diferente) aparentemente no hay forma documentada de detectar objetos mutables vs inmutables en general en el tiempo de ejecución. – StringSection

Respuesta

4

No hay forma (documentada) de determinar si una cadena es mutable en tiempo de ejecución o no.

Uno esperaría que una de las siguientes funcionaría, pero ninguno de ellos trabajan:

[[s class] isKindOfClass:[NSMutableString class]]; // always returns false 
[s isMemberOfClass:[NSMutableString class]]; // always returns false 
[s respondsToSelector:@selector(appendString)]; // always returns true 

Más información aquí, a pesar de que no le ayuda con el problema:

http://www.cocoabuilder.com/archive/cocoa/111173-mutability.html

+0

No funciona: NSMutableString * lala = @ "lala"; \t if ([lala isKindOfClass: [NSMutableString class]]) NSLog (@ "Clase mutable"); - muestra el mensaje de registro, sin embargo el objeto es inmutable – Vladimir

+0

Por supuesto que no funciona, porque el código es incorrecto. ** NSMutableString * lala = @ "lala" ** es un código ilegal. –

+0

Lo siento chicos - NSMutableString * myStringA = @ "A"; fue un error tipográfico - corregido ahora en el mensaje original. – StringSection

3

Si desea verificar la depuración, el siguiente código debería funcionar. La copia en el objeto inmutable es en sí misma, mientras que es una copia verdadera para los tipos mutables, en eso se basa el código. Tenga en cuenta que dado que llama al copy es lento, pero debería estar bien para la depuración. Si desea comprobar por algún otro motivo que no sea la depuración, consulte la respuesta de Rob (y olvídese de ello).

BOOL isMutable(id object) 
{ 
    id copy = [object copy]; 
    BOOL copyIsADifferentObject = (copy != object); 
    [copy release]; 
    return copyIsADifferentObject; 
} 

Descargo de responsabilidad: por supuesto, no hay garantía de que la copia sea equivalente a retener para los tipos inmutables. Puede estar seguro de que si isMutable devuelve NO, entonces no es mutable, por lo que la función probablemente se llame canBeMutable. En el mundo real, sin embargo, es una suposición bastante segura que los tipos inmutables (NSString, NSArray) implementarán esta optimización. Hay una gran cantidad de código que incluye cosas básicas como NSDictionary que espera una copia rápida de los tipos inmutables.

12

Los documentos incluyen una explicación bastante larga sobre por qué Apple no quiere que hagas esto y por qué explícitamente no lo admiten en Receiving Mutable Objects. El resumen es:

así que no hacen una decisión sobre la mutabilidad objeto sobre la base de lo que la introspección te dice acerca de un objeto. Trate los objetos como variables o no basados ​​en lo que le entregan en los límites de la API (es decir, según el tipo de devolución ). Si necesita marcar inequívocamente un objeto como mutable o inmutable cuando lo pase a clientes, pase esa información como un indicador junto con el objeto.

Encuentro que su ejemplo NSView es el más fácil de entender e ilustra un problema básico de Cocoa. Usted tiene un NSMutableArray llamado "elementos" que desea exponer como una matriz, pero no desea que las personas que llaman toquen. Tiene varias opciones:

  1. Exponga su NSMutableArray como NSArray.
  2. Siempre realice una copia no modificable cuando se lo solicite
  3. Almacene elementos como un NSArray y cree una nueva matriz cada vez que se produzca una mutación.

He hecho todo esto en varios puntos. # 1 es, de lejos, la solución más simple y rápida. También es peligroso, ya que la matriz puede mutar detrás de la persona que llama. Pero Apple indica que es lo que hacen en algunos casos (tenga en cuenta la advertencia para -subviews en NSView). Puedo confirmar que mientras que los números 2 y 3 son mucho más seguros, pueden crear problemas de rendimiento importantes, que es probablemente la razón por la cual Apple ha elegido no usarlos en miembros a los que se accede con frecuencia como, por ejemplo, las publicaciones.

El resultado de todo esto es que si utiliza el n. ° 1, la introspección le inducirá a error. Tiene un molde NSMutableArray como NSArray, y la introspección indicará que es mutable (la introspección no tiene forma de saber lo contrario). Pero no debes mutarlo. Solo el control de tipo de tiempo de compilación puede decírselo, por lo que es lo único en lo que puede confiar.

La solución para esto sería algún tipo de versión inmutable de copia rápida en la escritura de una estructura de datos mutable. De esa forma, el # 2 posiblemente podría hacerse con un rendimiento decente. Puedo imaginar cambios en el clúster de NSArray que permitan esto, pero no existe en Cocoa hoy en día (y podría afectar el rendimiento de NSArray en el caso normal, convirtiéndolo en un no iniciador). Incluso si lo tuviéramos, probablemente exista demasiado código que confíe en el comportamiento actual para permitir que se confíe en la introspección de la mutabilidad.

+1

Puede resolver el problema de rendimiento con el n. ° 3 implementando y exponiendo accesos de matriz mutante (http://developer.apple.com/documentation/Cocoa/Conceptual/ModelObjects/Articles/moAccessorMethods.html) y utilizándolos en otras clases. De esta forma, el acceso directo a la matriz mutable permanece privado, pero no estás creando y eliminando arreglos temporales. –

+1

@PH Esto es cierto y debería haber cubierto la solución de KVC. El problema que plantea es que hay muchos casos en que la persona que llama realmente necesita una colección (por lo general, para pasar a otra cosa que exige una). A menudo he resuelto esto al exponer algo como elementEnumerator, que la persona que llama puede convertirlo en una instantánea si así lo desea. El problema con este enfoque es la cantidad de código adicional requerido tanto para la persona que llama como para la llamada. Es esclarecedor que Apple, incluso en el nuevo código para UIView, eligió simplemente exponer las "subvistas" de NSMutableArray como un NSArray y no proporcionar un KVC completo. –

+0

Hm? No tengo claro de qué problema está hablando ni cuál es su solución. Publicación del blog y/o pastebin? –

Cuestiones relacionadas