NSMutableArray – заставить массив удерживать только определенный тип объекта

Есть ли способ заставить NSMutableArray удерживать только один тип конкретного объекта?

У меня есть определения classов следующим образом:

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

Как заставить массив жестких дисков удерживать объекты Wheel только с кодом? (и абсолютно не других объектов)

Обновление в 2015 году

Этот ответ был впервые написан в начале 2011 года и начался:

Нам действительно нужен параметрический polymorphism, чтобы вы могли объявить, скажем, NSMutableArray ; но, увы, такого нет.

В 2015 году Apple, по-видимому, изменила это с введением «легких генераторов» в Objective-C, и теперь вы можете заявить:

 NSMutableArray *onlyStrings = [NSMutableArray new]; 

Но все это не совсем то, что кажется, обратите внимание на «легкий» … Затем обратите внимание, что часть инициализации вышеуказанного объявления не содержит каких-либо общих обозначений. В то время как Apple ввела параметрические коллекции и добавила onlyStrings непосредственно к указанному выше массиву, onlyStrings , как говорят:

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

будет предупреждать о предупреждении, как указано, безопасность типа едва ли глубокая. Рассмотрим метод:

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

и fragment кода в другом методе того же classа:

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

То, что Apple внедрило, - это, по сути, система подсказок, помогающая автоматическому взаимодействию с Swift, который обладает вкусом универсальных генераторов. Однако на стороне Objective-C, в то время как компилятор предоставляет некоторые дополнительные подсказки, система «легкая», а целостность типа по-прежнему в конечном счете доходит до программиста - как и способ Objective-C.

Итак, что вы должны использовать? Новые легкие / псевдо-дженерики или разрабатывают собственные шаблоны для вашего кода? На самом деле нет правильного ответа, выяснить, что имеет смысл в вашем сценарии и использовать его.

Например: если вы ориентируетесь на взаимодействие с Swift, вы должны использовать легкие дженерики! Однако, если целостность типа коллекции важна в вашем сценарии, тогда вы можете комбинировать легкие дженерики с вашим собственным кодом на стороне Objective-C, которая обеспечивает целостность типа, которую Swift будет на своей стороне.

Остаток ответа 2011 года

Другим вариантом является быстрый общий подclass NSMutableArray, который вы начинаете с типа объекта, который вы хотите в своем monoморфном массиве. Этот параметр не дает вам статической проверки типов (в той мере, в какой вы когда-либо получали его в Obj-C), вы получаете исключения во время выполнения при вводе неправильного типа, так же, как вы получаете исключения во время выполнения для индекса за пределами границ и т. Д.

Это не проверено полностью и предполагает, что документация по переопределению NSMutableArray правильная ...

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

И реализация:

 @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 

Использовать как:

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

С генераторами XCode 7 теперь доступны в Objective-C!

Таким образом, вы можете объявить свой NSMutableArray как:

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

Компилятор даст вам предупреждение, если вы попытаетесь поместить объект без колес в свой массив.

Я могу ошибаться (я нооб), но я думаю, что если вы создадите собственный протокол и убедитесь, что объекты, которые вы добавляете в массив, следуют одному и тому же протоколу, тогда, когда вы объявляете массив, который вы используете

 NSArray 

Это должно препятствовать добавлению объектов, которые не следуют указанному протоколу.

как я знаю … перед добавлением какого-либо объекта в колесики mutableArray, вам нужно добавить некоторую галочку. Является ли объект, который я добавляю, является classом «колесо». если он затем добавляется, другой нет.

Пример:

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

Что-то вроде этого. Я не помню точный синтаксис.

Надеюсь, это поможет (и будет работать …: P)

Файл Wheel.h :

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

Файл car.h :

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

Файл car.m :

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

протокол может быть хорошей идеей:

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

использовать:

 NSArray* personArray; 

Xcode 7 позволяет вам определять массивы, словари и даже ваши собственные classы с наличием дженериков. Синтаксис массива выглядит следующим образом:

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

Я не верю, что это можно сделать с NSMutableArray из коробки. Вероятно, вы могли бы обеспечить это путем подclassификации и переопределения всех конструкторов и методов вставки, но это, вероятно, не стоит. Что вы надеетесь достичь этим?

Это невозможно; NSArray (независимо от того, изменен или нет) будет содержать любой тип объекта. Что вы можете сделать, так это создать свои собственные подclassы, как уже было предложено Джим. В качестве альтернативы, если вы хотите отфильтровать массив для удаления объектов, которые не соответствуют типу, вы можете сделать следующее:

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

Или сделайте другие суждения, основанные на результате isKindOfClass :, например, чтобы разделить массив, содержащий смесь Cars and Wheels, на два массива, каждый из которых содержит только один вид объекта.

Вы можете использовать nsexception, если у вас нет конкретного объекта.

 for (int i = 0; i 

Вот что я сделал, чтобы избежать подclassа NSMutableArray: используйте категорию. Таким образом, вы можете иметь нужные аргументы и возвращаемые типы. Обратите внимание на соглашение об именах: замените слово «объект» в каждом из методов, которые вы будете использовать с именем classа элемента. «objectAtIndex» становится «wheelAtIndex» и так далее. Таким образом, конфликт имен отсутствует. Очень аккуратно.

 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 

Существует проект с одним заголовком, который позволяет это: Objective-C-Generics

Использование :

Скопируйте ObjectiveCGenerics.h в свой проект. При определении нового classа используйте макрос GENERICSABLE.

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

Теперь вы можете использовать generics с массивами и наборами, как обычно, в Java, C # и т. Д.

Код: введите описание изображения здесь

  • Декларация о свойствах и автоматическое резервирование хранилища
  • Класс Foo реализован как в MyApp, так и в MyAppTestCase. Один из двух будет использован. Какой из них не определен
  • Могу ли я делать запросы POST или GET из приложения iphone?
  • Использование Objective C / Cocoa для unescape символов Unicode, то есть \ u1234
  • objective-C: Как получить адрес маршрутизатора?
  • Множественное перечисление значений в Obj-C
  • Получите данные Exif от UIImage - UIImagePickerController
  • UIWebView не масштабирует контент для соответствия
  • Рисование поэтапно в UIView (iPhone)
  • Сохранить видео Youtube для iPhone в приложении
  • Использование Swift CFunctionPointer для передачи обратного вызова API CoreMIDI
  • Давайте будем гением компьютера.