19 Mai, 2012 21:27

indexOfObject x isEqual - Mudanças no iOS

Existe um método em NSArray que permite encontrar a posição de um elemento fornecendo, como parâmetro, um objeto que seja "igual" a esse elemento: indexOfObject:. Quem programa em Objective C ou outra linguagem orientada a objetos sabe que ser "igual" e ser "o mesmo" são coisas diferentes. Ser "igual" significa que o método isEqual: (ou equivalente na sua linguagem) retorna true/YES/1 quando outro objeto é passado como parâmetro.

É baseado nessa premissa que o método indexOfObject: funciona. De acordo com a documentação:

indexOfObject: Returns the lowest index whose corresponding array value is equal to a given object.

  • (NSUInteger)indexOfObject:(id)anObject

Parameters: anObject

Return Value: The lowest index whose corresponding array value is equal to anObject. If none of the objects in the array is equal to anObject, returns NSNotFound.

Discussion: Starting at index 0, each element of the array is sent an isEqual: message until a match is found or the end of the array is reached. This method passes the anObject parameter to each isEqual: message. Objects are considered equal if isEqual: (declared in the NSObject protocol) returns YES.

Com esse conhecimento, a aplicação do meu amigo foi implementada (na época só havia o iOS até 4.x) usando esse método para encontrar a posição de um determinado objeto em uma lista.

Como a criação de um objeto "modelo" só para buscar um elemento da lista era trabalhosa, decidiu usar o estilo "duck typing" do Objective C e passar um objeto mais simples, como uma string, para o método indexOfObject:. Para finalizar, bastou implementar o método isEqual: na classe dos objetos que populam a lista.

Algo assim

- (BOOL)isEqual:(id)object {
    if ([object isKindOfClass:[Elemento class]]) {
        return [self.atrX isEqual:object.atrX];
    }
    else {
        return [self.atrX isEqual:[object description]];
    }
}

Com isso a busca da posição do elemento ficou tão simples quanto

[elementos indexOfObject:@"Teste"];

Funcionou muito bem. Até a chegada do iOS 5. De uma hora pra outra o método parou de funcionar. Com muita investigação, descobrimos o porquê.

Imagine objetos da classe A e da classe B. Ambos implementam o isEqual: como abaixo:

@implementation ClasseA

- (BOOL)isEqual:(id)object {
    NSLog(@"Classe A - isEqual");
    return ...; //Algum critério
}
@end

@implementation ClasseB

- (BOOL)isEqual:(id)object {
    NSLog(@"Classe B - isEqual");
    return ...; //Algum critério
}
@end

Temos um array com 3 objetos da classe A e então fazemos a chamada abaixo com um objeto da classe B.

[objetosA indexOfObject:objetoB];

Até o iOS 4, se observássemos o console, veremos uma saída semelhante a abaixo:

2012-05-19 12:23:34.592 TestesIndexOf[1786:307] Classe A - isEqual
2012-05-19 12:23:34.599 TestesIndexOf[1786:307] Classe A - isEqual
2012-05-19 12:23:34.602 TestesIndexOf[1786:307] Classe A - isEqual

Agora, no iOS 5, a saída ficou:

2012-05-19 12:13:50.311 TestesIndexOf[572:207] Classe B - isEqual
2012-05-19 12:13:50.314 TestesIndexOf[572:207] Classe B - isEqual
2012-05-19 12:13:50.315 TestesIndexOf[572:207] Classe B - isEqual

Repare que de uma versão para a outra do iOS, o comportamento esperado do indexOfObject: mudou. Ao invés de perguntar aos elementos do array se o parâmetro é "igual" a eles, a nova implementação pergunta ao objeto do parâmetro se os elementos do array são "iguais" a ele.

Parece uma mudança boba, mas mostra o quanto devemos nos preocupar quando a Apple libera uma nova versão do iOS e executar todos os testes possíveis para garantir que tudo continue funcionando como antes.

E você? Já teve dor-de-cabeça com alguma mudança inesperada de um iOS para o outro?

Ao navegar neste site, você consente o uso de cookies nossos e de terceiros, que coletam informações anônimas e são essenciais para melhorar sua experiência em nosso site.