NSMutableArray-wymusza, aby tablica zawierała tylko określony typ obiektu

Czy istnieje sposób, aby zmusić NSMutableArray do trzymania tylko jednego określonego typu obiektu?

Mam następujące definicje klas:

@interface Wheel:NSObject  
{    
  int size;  
  float diameter;  
}  
@end  


@interface Car:NSObject  
{  
   NSString *model;  
   NSString *make;  
   NSMutableArray *wheels;  
}  
@end

Jak zmusić kołado trzymania Koła obiektów tylko z kodem? (i absolutnie nie inne obiekty)

Author: vikingosegundo, 2011-03-04

12 answers

Aktualizacja w 2015

Odpowiedź została po raz pierwszy napisana na początku 2011 roku i zaczęła się:

Tak naprawdę chcemy polimorfizmu parametrycznego, aby można było zadeklarować, powiedzmy, NSMutableArray<NSString>; ale niestety taki nie jest dostępny.

W 2015 roku Apple najwyraźniej zmieniło to, wprowadzając "lekkie generyki" do Objective-C i teraz możesz zadeklarować:

NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new];

Ale wszystko nie jest tak, jak się wydaje, zauważ "lekki"... Następnie zauważ, że część inicjalizacyjna powyższej deklaracji nie zawiera żadnej notacji ogólnej. Podczas gdy Apple wprowadziło zbiory parametryczne i dodało non-string bezpośrednio do powyższej tablicy, onlyStrings, jak np.:

[onlyStrings addObject:@666]; // <- Warning: Incompatible pointer types...
/ Align = "left" / Rozważmy metodę:
- (void) push:(id)obj onto:(NSMutableArray *)array
{
   [array addObject:obj];
}

I fragment kodu w innej metodzie tej samej klasy:

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

To, co zaimplementowało Apple, jest zasadniczo systemem podpowiedzi do wspomagaj automatyczną współpracę z Swift, który ma smak bezpiecznych generyków. Jednak po stronie Objective-C, podczas gdy kompilator dostarcza dodatkowych wskazówek, system jest "lekki", a integralność typu nadal pozostaje w gestii programisty - podobnie jak sposób Objective-C.

Więc czego powinieneś użyć? Nowe lightweight / pseudo generics, czy wymyślać własne wzory dla kodu? Naprawdę nie ma właściwej odpowiedzi, dowiedzieć się, co ma sens w Twoim scenariuszu i użyć to.

Na przykład: jeśli kierujesz interakcję za pomocą Swift, powinieneś użyć lekkich leków generycznych! Jeśli jednak integralność typu zbioru jest ważna w Twoim scenariuszu, możesz połączyć lekkie generyki z własnym kodem po stronie Objective-C, co wymusza integralność typu, którą Swift będzie po swojej stronie.

Pozostałe odpowiedzi 2011

Jako kolejna opcja tutaj jest szybki ogólny podklasa NSMutableArray, który można init z rodzaju obiektu, który chcesz w tablicy monomorficznej. Opcja ta nie pozwala na statyczne sprawdzanie typu (w takim stopniu, w jakim kiedykolwiek dostajesz to w Obj-C), dostajesz wyjątki runtime przy wstawianiu niewłaściwego typu, tak jak dostajesz wyjątki runtime dla indeksu poza granicami itd.

Jest to Nie dokładnie przetestowane i zakłada, że dokumentacja nadpisująca NSMutableArray jest poprawna...

@interface MonomorphicArray : NSMutableArray
{
    Class elementClass;
    NSMutableArray *realArray;
}

- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems;
- (id) initWithClass:(Class)element;

@end

I realizacja:

@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 subclasses, 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

Użyj jako:

MonomorphicArray *monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3];

[monoString addObject:@"A string"];
[monoString addObject:[NSNumber numberWithInt:42]]; // will throw
[monoString addObject:@"Another string"];
 88
Author: CRD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 08:26:46

Z Xcode 7 generyczne są teraz dostępne w Objective-C!

Więc możesz zadeklarować swoje NSMutableArray jako:

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

Kompilator wyświetli ostrzeżenie, jeśli spróbujesz umieścić obiekt bez koła w tablicy.

 27
Author: andreacipriani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-12 16:19:30

I could be wrong (I ' m a noob), but I think, if you create a custom protocol and make sure the objects you are added to the same protocol, then when you declaration the array you use

NSArray<Protocol Name>

To powinno zapobiegać dodawaniu obiektów, które nie są zgodne ze wspomnianym protokołem.

 11
Author: Gravedigga,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-17 07:18:46

Z tego co wiem.. zanim dodasz jakiś obiekt w wheels mutableArray, musisz dodać jakiś znacznik wyboru. Jest obiektem, który dodaję jest klasa "koło". jeśli jest to dodać, inne mądre nie.

Przykład:

if([id isClassOf:"Wheel"] == YES)
{
[array addObject:id) 
}
Coś w tym stylu. nie pamiętam dokładnej składni.
 5
Author: harshit2811,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-19 01:01:02

Mam nadzieję, że to pomoże (i zadziała... : P)

Koło.h Plik:

@protocol Wheel
@end

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

Samochód.h Plik:

#import "Wheel.h"
@interface Car:NSObject  

{  
   NSString *model;  
   NSString *make;  
   NSMutableArray<Wheel, Optional> *wheels;  
}  
@end

Samochód.plik:

#import "Car.h"
@implementation Car

-(id)init{
   if (self=[super init]){
   self.wheels = (NSMutableArray<Wheel,Optional>*)[NSMutableArray alloc]init];
   }
return self;
}
@end
 3
Author: Aviram Net,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-25 13:35:07

Protokół może dobry pomysł:

@protocol Person <NSObject>
@end

@interface Person : NSObject <Person>
@end

Do użycia:

NSArray<Person>*  personArray;
 2
Author: lbsweek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-05 09:13:34

Xcode 7 pozwala definiować Tablice, Słowniki, a nawet własne klasy jako posiadające generyki. Składnia tablicy jest następująca:

NSArray<NSString*>* array = @[@"hello world"];
 2
Author: Brian Trzupek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-02 11:27:40

Nie wierzę, że jest jakiś sposób, aby to zrobić z NSMutableArray out of the box. Prawdopodobnie możesz to wymusić poprzez podklasowanie i nadpisanie wszystkich konstruktorów i metod wstawiania, ale prawdopodobnie nie jest to tego warte. Co chcesz z tym osiągnąć?

 1
Author: Jim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 18:02:23

Nie jest to możliwe; nsArray (zmienny lub nie) utrzyma dowolny typ obiektu. Co możesz zrobić, to stworzyć własne podklasy niestandardowe, jak już zasugerował Jim. Alternatywnie, jeśli chcesz filtrować tablicę, aby usunąć obiekty, które nie były tego typu, możesz to zrobić:

- (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]];

Lub dokonać innych osądów na podstawie wyniku isKindOfClass:, np. podzielić tablicę zawierającą mieszaninę samochodów i kół na dwie tablice, z których każda zawiera tylko jeden rodzaj obiekt.

 0
Author: Tommy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-04 18:04:29

Możesz użyć nsexception, jeśli nie masz określonego obiektu.

for (int i = 0; i<items.count;i++) {
 if([[items objectAtIndex:i] isKindOfClass:[Wheel class]])
 {
  // do something..!
 }else{
  [NSException raise:@"Invalid value" format:@"Format of %@ is invalid", items];
  // do whatever to handle or raise your exception.
 }
}
 0
Author: Lalith B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-20 05:04:42

Oto coś, co zrobiłem, aby uniknąć podklasowania NSMutableArray: użyj kategorii. W ten sposób możesz mieć żądane typy argumentów i zwrotów. Zwróć uwagę na konwencję nazewnictwa: zastąp słowo "obiekt" w każdej z metod, których użyjesz nazwą klasy elementu. "objectAtIndex" staje się "wheelAtIndex" i tak dalej. W ten sposób nie ma konfliktu nazw. Bardzo schludny.

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
 0
Author: Charles Gillingham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 06:28:39

Istnieje jeden projekt pliku nagłówkowego, który pozwala na: Objective-C-Generics

Użycie :

Copy ObjectiveCGenerics.h do twojego projektu. Podczas definiowania nowej klasy użyj makra GENERICSABLE.

#import "ObjectiveCGenerics.h"

GENERICSABLE(MyClass)

@interface MyClass : NSObject<MyClass>

@property (nonatomic, strong) NSString* name;

@end

Teraz możesz używać generyków z tablicami i zestawami tak, jak to zwykle robisz w Javie, C#, itp.

Kod: Tutaj wpisz opis obrazka

 0
Author: Misha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-12 12:42:33