Comprender la comparación de NSString

83

Ambas comparaciones siguientes se evalúan como verdaderas:

1)

@"foo" == @"foo";

2)

NSString *myString1 = @"foo";
NSString *myString2 = @"foo";
myString1 == myString2;

Sin embargo, definitivamente hay ocasiones en las que dos NSStrings no se pueden comparar con el operador de igualdad y, en [myString1 isEqualToString:myString2]su lugar , se requieren. ¿Alguien puede arrojar algo de luz sobre esto?

Yarin
fuente

Respuestas:

165

La razón por la que ==funciona es debido a la comparación de punteros. Cuando define un NSStringuso constante @"", el compilador uniquifica la referencia. Cuando se definen las mismas constantes en otros lugares de su código, todas apuntarán a la misma ubicación real en la memoria.

Al comparar NSStringinstancias, debe utilizar el isEqualToString:método:

NSString *myString1 = @"foo";
NSString *myString2 = @"foo";
NSString *myString3 = [[NSString alloc] initWithString:@"foo"];
NSLog(@"%d", (myString2 == myString3))  //0
NSLog(@"%d", (myString1 == myString2)); //1
NSLog(@"%d", [myString1 isEqualToString:myString2]); //1
NSLog(@"%d", [myString1 isEqualToString:myString3]); //1
[myString3 release];

Editar:

NSString *myString3 = [[NSString alloc] initWithString:@"foo"]; 
// this is same with @"foo"

initWithString:ya no crea una nueva referencia, necesitará initWithFormat,

NSString *myString3 = [[NSString alloc] initWithFormat:@"foo"];
Jacob Relkin
fuente
6
La mayoría de los compiladores también harán myString3un puntero a la constante "foo"como una optimización, por lo que, en general, estas tres variables apuntarán a la misma ubicación de memoria. Esto es cierto tanto para gcc como para clang (con opciones predeterminadas). Intente compilar esto: gist.github.com/578568
mipadi
y entonces, ¿cómo puedo hacer que una variable NSString se comporte exactamente como @ "..."? la razón por la que pregunto es b / c en mi código en este momento, la constante @ ".." funciona pero se bloquea tan pronto como la reemplazo con una variable NSString .. ver aquí
abbood
2
+1, solo para agregar: isEqual:de hecho, hace una comparación de cadena completa y devuelve el mismo resultado isEqualToStringporque la Referencia de protocolo NSObject y la Referencia de clase NSString especifican explícitamente (respectivamente): "Si dos objetos son iguales (por -isEqual:), deben tener el mismo valor hash "Y" Si dos objetos de cadena son iguales (según lo determinado por el método isEqualToString:), deben tener el mismo valor hash ".
Ephemera
13

El operador de igualdad ==solo compara direcciones de puntero. Cuando crea dos cadenas idénticas usando la @""sintaxis literal , el compilador detectará que son iguales y solo almacenará los datos una vez. Por lo tanto, los dos punteros apuntan a la misma ubicación. Sin embargo, las cadenas creadas por otros medios pueden contener datos idénticos, pero almacenarse en diferentes ubicaciones de memoria. Por lo tanto, siempre debe usarlo isEqual:al comparar cadenas.

Tenga en cuenta que isEqual:y isEqualToString:siempre devuelve el mismo valor, pero isEqualToString:es más rápido.

David M.
fuente
2
También tenga en cuenta que isEqualToString: causará una excepción si el parámetro que se le pasa es nil. Entonces, si existe la posibilidad de que esté comparando con una cadena nula, primero debe hacer una verificación nula o usarisEqual:
Sandy Chapman
10

==compara ubicaciones en la memoria. ptr == ptr2si ambos apuntan a la misma ubicación de memoria. Esto sucede que funciona con constantes de cadena porque el compilador usa una cadena real para constantes de cadena idénticas. No funcionará si tiene variables con el mismo contenido, porque apuntarán a diferentes ubicaciones de memoria; utilizar isEqualToStringen tal caso.

mipadi
fuente
¿Puede iluminar con un ejemplo de lo que quiere decir "no funcionará si tiene variables con el mismo contenido"
Logicsaurus Rex
6

En Cocoa, las cadenas se comparan utilizando el isEqualToString:método de NSString .

La comparación de punteros funciona en su caso porque el compilador es lo suficientemente suave como para fusionar los dos literales de cadena para apuntar a un objeto. No hay garantía de que dos cadenas idénticas compartan una NSStringinstancia.

Nikolai Ruhe
fuente
¿Tiene alguna referencia oficial a esto? "No hay garantía de que dos cadenas idénticas compartan una instancia de NSString".
Logicsaurus Rex
@ user3055655 No necesito una referencia: puede escribir código fácilmente que crea dos NSStringinstancias distintas con contenido idéntico:[NSMutableString string] != [NSMutableString string]
Nikolai Ruhe
@ user3055655 Si quiere decir que mi afirmación no es cierta para los literales de cadena: pruebe los literales de dos paquetes (como una aplicación y su paquete de pruebas).
Nikolai Ruhe
Solo quería algo para mostrar a mis compañeros de trabajo. No esperaría que las cadenas mutables fueran iguales, pero declarar dos instancias de NSString y asignar algún @ "valor de cadena" siempre garantiza la ==funcionalidad. Sin embargo, si elimina una NSString, asigna un valor y luego elimina otra NSString como esta NSString stringWithFormat:, de hecho obtendrá dos cadenas diferentes que ==fallarán. Dijiste que no hay garantía de que dos instancias de NSString (no NSMutableString) compartan una instancia de NSString, y simplemente te pregunté si tenías alguna prueba de esa afirmación para poder compartirla.
Logicsaurus Rex
@ user3055655 Como dije, pruebe literales de distintos paquetes.
Nikolai Ruhe
3

Un ejemplo que demuestra cómo se romperá la comparación de direcciones como sustituto de la comparación de cadenas:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSString *s1 = @"foo";
    NSString *s2 = @"foo";
    NSString *s3 = [[[NSString alloc] initWithString:@"foo"] autorelease];
    NSMutableString *s4 = [NSMutableString stringWithString:@"foobar"];
    [s4 replaceOccurrencesOfString:@"bar"
                        withString:@""
                           options:NSLiteralSearch
                             range:NSMakeRange(0, [s4 length])];

    NSLog(@"s1 = %p\n", s1);
    NSLog(@"s2 = %p\n", s2);
    NSLog(@"s3 = %p\n", s3);
    NSLog(@"s4 = %p\n", s4); // distinct from s1

    NSLog(@"%i", [s1 isEqualToString:s4]); // 1

    [pool release];
SK9
fuente
0

Mira este ejemplo:

NSString *myString1 = @"foo";
NSMutableString *myString2 = [[NSMutableString stringWithString:@"fo"] stringByAppendingString: @"o"];

NSLog(@"isEquality: %@", ([myString1 isEqual:myString2]?@"+":@"-")); //YES
NSLog(@"isEqualToStringity: %@", ([myString1 isEqualToString:myString2]?@"+":@"-")); //YES
NSLog(@"==ity: %@", ((myString1 == myString2)?@"+":@"-")); // NO

Por lo tanto, es probable que el compilador use el método isEqualToString para procesar isEquals para los punteros de NSString y desreferenciar, aunque no tenía que hacerlo. Y los indicadores son diferentes, como ve.

Dmitry Zvorikin
fuente
-1
  NSString *str1=[NSString stringWithFormat:@"hello1"];
    NSString *str2=[NSString stringWithFormat:@"hello1"];
    NSString *str3 = [[NSString alloc] initWithString:@"hello1"];




// == compares the pointer but in our example we are taking same string value to different object  using @  so it will point to same address so output will be TRUE condition
    if (str1==str2) {
        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");
    }


    // == compares the pointer but in our example we are taking same string value to different object but we have allocated different string so both object will pount to different address so output will be FALSE condition
    if (str1==str3) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }


  // compare:= compares the values of objects so output will be TRUE condition
    if ([str1 compare:str3]== NSOrderedSame) {
        NSLog(@"Both String are equal");

    }
    else{
        NSLog(@"Both String not are equal");

    }


    // isEqual compares the values of objects so output will be TRUE condition

    if ([str1 isEqual:str2]) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }

    // isEqual compares the values of objects so output will be TRUE condition

    if ([str1 isEqual:str3]) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }


    // isEqualToString compares the values of objects so output will be TRUE condition
    if ([str1 isEqualToString:str2]) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }


    // isEqualToString compares the values of objects so output will be TRUE condition
    if ([str1 isEqualToString:str3]) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }

    // == compares the pointers since we have initialized the same value to first object so the pointer be be same for same value so output will be TRUE condition
    if (str1==@"hello1") {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }
Manoj Singhal
fuente