2015年的更新
这篇答案最初是在2011年早期写的,开头是这样的:
我们真正想要的是参数多态性,这样你就可以声明诸如 NSMutableArray<NSString>
;但遗憾的是目前并不支持。
2015年苹果引入了“轻量级泛型”到Objective-C中,似乎改变了这种情况,现在你可以声明:
NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new]
但这并不完全是看起来的样子,注意 "轻量级" ... 然后注意上述声明的初始化部分没有包含任何通用符号。尽管苹果公司引入了参数化集合,并且可以将非字符串
直接 添加到上述数组中,例如:
onlyStrings
。
[onlyStrings addObject:@666]
运行该方法会触发如上所示的警告,类型安全性不够严密。考虑下面的方法:
- (void) push:(id)obj onto:(NSMutableArray *)array
{
[array addObject:obj];
}
在同一类的另一个方法中的代码片段:
NSMutableArray<NSString *> *oops = [NSMutableArray new]
[self push:@"asda" onto:oops]
[self push:@42 onto:oops]
苹果实现的是一种提示系统,用于辅助与Swift自动交互,它确实具有类型安全泛型的风味。然而,在Objective-C方面,虽然编译器提供了一些额外的提示,但这个系统是“轻量级”的,类型完整性最终仍取决于程序员 - 这是Objective-C的方式。
那么你应该使用哪个呢?新的轻量级/伪泛型,还是为您的代码设计自己的模式?真的没有正确答案,找出在您的情况下有意义的内容并使用它。
例如:如果您的目标是与Swift进行交互,您应该使用轻量级泛型!但是,如果集合的类型完整性在您的情况下很重要,则可以将轻量级泛型与在Objective-C端强制执行Swift将在其端执行的类型完整性的自己的代码相结合。
作为另一个选项,这里有一个快速的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;
}
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if ([anObject isKindOfClass:elementClass])
{
[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];
}
- (NSUInteger) count
{
return [realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [realArray objectAtIndex:index];
}
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]];
[monoString addObject:@"Another string"];