Ao executar este código em Objective-C, o resultado é bastante inesperado.
NSArray *array = [NSArray arrayWithObjects:@1, @2, nil];
NSLog(@"%@", [array class]);
// saída: __NSArrayI
Embora tenhamos criado um NSArray, o nome da classe é __NSArrayI. Para um array vazio, pode surgir __NSArray0; com um elemento, __NSSingleObjectArrayI.
Não é um bug. Esse é o padrão de cluster de classes (Class Cluster).
NSArray, NSString, NSNumber e NSDictionary são todos classes abstratas.
As instâncias reais são criadas como subclasses ocultas, conforme a situação.
É o assunto do item 9 de Effective Objective-C 2.0 e uma estrutura inevitável enquanto você usar Foundation.
Resumo do essencial (3 pontos)
- Coleções do Foundation, como NSArray, são classes abstratas que expõem apenas a interface pública.
- O inicializador escolhe e retorna uma instância de uma subclasse privada (uma espécie de padrão de fábrica).
- Por isso, a
[array class]comparação é perigosa, e a criação de subclasses segue regras rigorosas.
Por que fazer assim?
Até um único array pode exigir várias estratégias de otimização.
- Array imutável com 0 elementos: um singleton basta, sem criar outro a cada vez
- 1 a 2 elementos: inserir os ponteiros na struct é mais rápido
- Array mutável grande: requer uma implementação separada com estratégia de realocação
Em vez de enfiar tudo em ifs na implementação de NSArray, o Foundation mantém subclasses específicas para cada cenário, e o método de fábrica retorna a adequada. O código cliente conhece apenas a interface de NSArray; a implementação interna pode ser trocada livremente entre versões do sistema operacional.
Na prática, [NSArray alloc] retorna um objeto temporário chamado __NSPlaceholderArray, que vira a implementação real nas chamadas seguintes da família init. Embora seja comum encadear alloc e init, em um cluster de classes essas duas etapas passam por objetos diferentes.
Duas armadilhas comuns no dia a dia
Armadilha 1: comparar classes diretamente
// Código perigoso
if ([object class] == [NSArray class]) {
// Nunca entra aqui, porque as classes reais são __NSArrayI , entre outras.
}
// Código correto
if ([object isKindOfClass:[NSArray class]]) {
// Abrange todo o cluster na verificação
}
Os membros do cluster são subclasses da classe pública. Portanto, consulte a família com isKindOfClass:, em vez de comparar igualdade.
Armadilha 2: criar subclasses sem cuidado
Parece bastar herdar de NSArray e sobrescrever um método, mas essa subclasse nem sequer herda armazenamento: classes abstratas não têm storage. Para integrá-la corretamente ao cluster, é preciso seguir as regras.
- Providencie seu próprio armazenamento (normalmente, mantendo um NSArray real internamente)
- Implemente todos os métodos primitivos exigidos (count e objectAtIndex: no caso de NSArray)
Os demais métodos operam sobre esses primitivos, então implementar apenas os dois ativa todos os recursos do cluster. Ainda assim, o conselho de Effective Objective-C é claro: na maioria dos casos, composição (uma classe wrapper que contém um NSArray) é melhor que herança.
Por que isso também importa para quem desenvolve em Swift
Array em Swift é uma struct e parece não ter relação com o padrão, mas há dois pontos de contato.
O primeiro é o bridging. Quando um NSArray vindo de uma API Objective-C vira um Swift Array, implementações reais como _ContiguousArrayStorage ou __NSArrayI continuam por trás. Ao investigar problemas de desempenho do bridging, você acaba encontrando essas implementações.
O segundo é que a própria ideia chegou ao Swift. “O tipo público expõe apenas a interface, e um tipo oculto cuida da implementação” aparece em wrappers de type erasure como AnySequence e na palavra-chave some (tipos opacos). O tipo retornado é único, mas só o compilador e a implementação conhecem o tipo real: a mesma linhagem conceitual dos clusters de classes.
Resumo
- NSArray, NSString, NSNumber e NSDictionary são classes abstratas; as instâncias reais são subclasses ocultas
- Para verificar tipos, use sempre isKindOfClass:; nunca compare a igualdade das classes
- Subclasses de um cluster precisam implementar os métodos primitivos; em geral, composição é melhor
- Separar interface e implementação é uma ideia clássica que chega até os tipos opacos do Swift
Mesmo ao encontrar no depurador uma classe com o prefixo desconhecido __NS, não há motivo para pânico. É apenas a otimização refinada pelo Foundation ao longo de quase 30 anos, agora visível.

