Обновление в 2015 году
Этот ответ был впервые написан в начале 2011 года и начал:
Что нам действительно нужно, так это параметрический полиморфизм, чтобы вы могли объявить, скажем, NSMutableArray<NSString>
; но увы такого нет в наличии.
В 2015 году Apple, очевидно, изменила это с введением «облегченных обобщений» в Objective-C, и теперь вы можете объявить:
NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new];
Но все не совсем так, как кажется, обратите внимание на "облегченный" ... Тогда обратите внимание, что часть инициализации в вышеприведенном объявлении не содержит общих обозначений. В то время как Apple представила параметрические коллекции и добавив нестандартную строку непосредственно к вышеуказанному массиву, onlyStrings
, как сказано:
[onlyStrings addObject:@666]; // <- Warning: Incompatible pointer types...
выдаст предупреждение, как указано, безопасность типа едва зашита. Рассмотрим метод:
- (void) push:(id)obj onto:(NSMutableArray *)array
{
[array addObject:obj];
}
и фрагмент кода в другом методе того же класса:
NSMutableArray<NSString *> *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 года
В качестве другого варианта здесь приведен быстрый общий подкласс NSMutableArray, который вы инициируете с типом объекта, который вы хотите в своем мономорфном массиве. Эта опция не дает вам статической проверки типов (в той степени, в которой вы когда-либо ее получали в 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"];