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?