NSMutableArray – força a matriz a manter somente o tipo de object específico

Existe uma maneira de forçar NSMutableArray para manter apenas um tipo de object específico?

Eu tenho definições de classs como segue:

@interface Wheel:NSObject { int size; float diameter; } @end @interface Car:NSObject { NSString *model; NSString *make; NSMutableArray *wheels; } @end 

Como posso forçar a matriz de rodas a manter objects Wheel apenas com código? (e absolutamente não outros objects)

Atualize em 2015

Esta resposta foi escrita pela primeira vez no início de 2011 e começou:

O que realmente queremos é o polymorphism paramétrico para que você possa declarar, digamos, NSMutableArray ; mas, infelizmente, tal não está disponível.

Em 2015, a Apple aparentemente mudou isso com a introdução de “genéricos leves” no Objective-C e agora você pode declarar:

 NSMutableArray *onlyStrings = [NSMutableArray new]; 

Mas tudo não é bem o que parece, observe o “leve” … Então, observe que a parte de boot da declaração acima não contém qualquer notação genérica. Enquanto a Apple introduziu collections paramétricas, e adicionando uma não string diretamente ao array acima, onlyStrings , como em dizer:

 [onlyStrings addObject:@666]; // < - Warning: Incompatible pointer types... 

irá ilicitar o aviso como indicado, o tipo de segurança é apenas superficial. Considere o método:

 - (void) push:(id)obj onto:(NSMutableArray *)array { [array addObject:obj]; } 

e o fragment de código em outro método da mesma class:

 NSMutableArray *oops = [NSMutableArray new]; [self push:@"asda" onto:oops]; // add a string, fine [self push:@42 onto:oops]; // add a number, no warnings... 

O que a Apple implementou é essencialmente um sistema insinuante para auxiliar na interação automática com o Swift, que tem um sabor de genéricos seguros para o tipo. No entanto, no lado do Objective-C, enquanto o compilador fornece algumas dicas extras, o sistema é "leve" e a integridade do tipo ainda é fundamental para o programador - assim como o modo Objective-C.

Então, qual você deve usar? Os novos lightweight / pseudo generics, ou conceber seus próprios padrões para o seu código? Realmente não há resposta certa, descobrir o que faz sentido em seu cenário e usá-lo.

Por exemplo: se você está direcionando a interoperação com o Swift, você deve usar os genéricos leves! No entanto, se a integridade do tipo de uma coleção for importante em seu cenário, você poderá combinar os genéricos leves com seu próprio código no lado do Objective-C, que reforça a integridade de tipo que o Swift estará ao seu lado.

O restante da resposta de 2011

Como outra opção aqui está uma subclass geral rápida de NSMutableArray que você inicia com o tipo de object que você quer em seu array monomórfico. Esta opção não fornece verificação de tipo estática (na medida em que você a obtiver em Obj-C), você obtém exceções de tempo de execução ao inserir o tipo incorreto, assim como obtém exceções de tempo de execução para índices fora dos limites etc.

Isso não é totalmente testado e pressupõe que a documentação sobre a substituição do NSMutableArray está correta ...

 @interface MonomorphicArray : NSMutableArray { Class elementClass; NSMutableArray *realArray; } - (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems; - (id) initWithClass:(Class)element; @end 

E a implementação:

 @implementation MonomorphicArray - (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems { elementClass = element; realArray = [NSMutableArray arrayWithCapacity:numItems]; return self; } - (id) initWithClass:(Class)element { elementClass = element; realArray = [NSMutableArray new]; return self; } // override primitive NSMutableArray methods and enforce monomorphism - (void) insertObject:(id)anObject atIndex:(NSUInteger)index { if ([anObject isKindOfClass:elementClass]) // allows subclasss, use isMemeberOfClass for exact match { [realArray insertObject:anObject atIndex:index]; } else { NSException* myException = [NSException exceptionWithName:@"InvalidAddObject" reason:@"Added object has wrong type" userInfo:nil]; @throw myException; } } - (void) removeObjectAtIndex:(NSUInteger)index { [realArray removeObjectAtIndex:index]; } // override primitive NSArray methods - (NSUInteger) count { return [realArray count]; } - (id) objectAtIndex:(NSUInteger)index { return [realArray objectAtIndex:index]; } // block all the other init's (some could be supported) static id NotSupported() { NSException* myException = [NSException exceptionWithName:@"InvalidInitializer" reason:@"Only initWithClass: and initWithClass:andCapacity: supported" userInfo:nil]; @throw myException; } - (id)initWithArray:(NSArray *)anArray { return NotSupported(); } - (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { return NotSupported(); } - (id)initWithContentsOfFile:(NSString *)aPath { return NotSupported(); } - (id)initWithContentsOfURL:(NSURL *)aURL { return NotSupported(); } - (id)initWithObjects:(id)firstObj, ... { return NotSupported(); } - (id)initWithObjects:(const id *)objects count:(NSUInteger)count { return NotSupported(); } @end 

Use como:

 MonomorphicArray *monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3]; [monoString addObject:@"A string"]; [monoString addObject:[NSNumber numberWithInt:42]]; // will throw [monoString addObject:@"Another string"]; 

Com o XCode 7, os genéricos estão agora disponíveis no Objective-C!

Então você pode declarar seu NSMutableArray como:

 NSMutableArray  *wheels = [[NSMutableArray alloc] initWithArray:@[[Wheel new],[Wheel new]]; 

O compilador lhe dará um aviso se você tentar colocar o object não-Wheel em sua matriz.

Eu poderia estar errado (eu sou um noob), mas eu acho, se você criar um protocolo personalizado e se certificar de que os objects que você está adicionando ao array siga o mesmo protocolo, então quando você declarar o array que você usa

 NSArray 

Isso deve impedir que objects sejam adicionados que não sigam o protocolo mencionado.

como eu sei .. antes de você adicionou qualquer object em rodas mutableArray, você tem que adicionar alguma marca de seleção. É o object que estou adicionando é class “roda”. se é então adicionar, outro sábio não.

Exemplo:

 if([id isClassOf:"Wheel"] == YES) { [array addObject:id) } 

Algo assim. Eu não lembro a syntax exata.

Espero que isso ajude (e trabalhe …: P)

Arquivo Wheel.h :

 @protocol Wheel @end @interface Wheel : NSObject @property ... @end 

Arquivo Car.h :

 #import "Wheel.h" @interface Car:NSObject { NSString *model; NSString *make; NSMutableArray *wheels; } @end 

Arquivo Car.m :

 #import "Car.h" @implementation Car -(id)init{ if (self=[super init]){ self.wheels = (NSMutableArray*)[NSMutableArray alloc]init]; } return self; } @end 

protocolo talvez seja uma boa ideia:

 @protocol Person  @end @interface Person : NSObject  @end 

usar:

 NSArray* personArray; 

O Xcode 7 permite que você defina Arrays, Dicionários e até mesmo suas próprias Classes como tendo genéricos. A syntax da matriz é a seguinte:

 NSArray* array = @[@"hello world"]; 

Eu não acredito que haja alguma maneira de fazer isso com o NSMutableArray fora da checkbox. Você provavelmente poderia reforçar isso subclassificando e substituindo todos os construtores e methods de inserção, mas provavelmente não vale a pena. O que você espera conseguir com isso?

Isso não é possível; um NSArray (seja mutável ou não) manterá qualquer tipo de object. O que você pode fazer é criar suas próprias subclasss personalizadas, como já sugerido por Jim. Alternativamente, se você quisesse filtrar uma matriz para remover objects que não eram do tipo que você quer, então você poderia fazer:

 - (void)removeObjectsFromArray:(NSMutableArray *)array otherThanOfType:(Class)type { int c = 0; while(c < [array length]) { NSObject *object = [array objectAtIndex:c]; if([object isKindOfClass:type]) c++; else [array removeObjectAtIndex:c]; } } ... [self removeObjectsFromArray:array otherThanOfType:[Car class]]; 

Ou faça outros julgamentos baseados no resultado de isKindOfClass :, por exemplo, para dividir um array contendo uma mistura de Cars e Wheels em dois arrays, cada um contendo apenas um tipo de object.

Você pode usar o nsexception se você não tiver o object específico.

 for (int i = 0; i 

Aqui está algo que fiz para evitar subclasss de NSMutableArray: use uma categoria. Dessa forma, você pode ter o argumento e retornar os tipos desejados. Observe a convenção de nomenclatura: substitua a palavra “object” em cada um dos methods que você usará com o nome da class do elemento. “objectAtIndex” se torna “wheelAtIndex” e assim por diante. Desta forma não há conflitos de nome. Muito arrumado.

 typedef NSMutableArray WheelList; @interface NSMutableArray (WheelList) - (wheel *) wheelAtIndex: (NSUInteger) index; - (void) addWheel: (wheel *) w; @end @implementation NSMutableArray (WheelList) - (wheel *) wheelAtIndex: (NSUInteger) index { return (wheel *) [self objectAtIndex: index]; } - (void) addWheel: (wheel *) w { [self addObject: w]; } @end @interface Car : NSObject @property WheelList *wheels; @end; @implementation Car @synthesize wheels; - (id) init { if (self = [super init]) { wheels = [[WheelList alloc] initWithCapacity: 4]; } return self; } @end 

Existe um projeto de arquivo de header único que permite isso: Objective-C-Generics

Uso :

Copie ObjectiveCGenerics.h para o seu projeto. Ao definir uma nova class, use a macro GENERICSABLE.

 #import "ObjectiveCGenerics.h" GENERICSABLE(MyClass) @interface MyClass : NSObject @property (nonatomic, strong) NSString* name; @end 

Agora você pode usar genéricos com matrizes e conjuntos, como faz normalmente em Java, C #, etc.

Código: insira a descrição da imagem aqui