我想知道Objective-C是否支持泛型?
例如,考虑一个方法:
-(void) sort: (NSMutableArray *) deck {
}
有没有办法让它只处理一副纸牌?
可以强制执行这样的规定吗?
-(void) sort: (NSMutableArray <Card *>) deck {
}
我想知道Objective-C是否支持泛型?
例如,考虑一个方法:
-(void) sort: (NSMutableArray *) deck {
}
有没有办法让它只处理一副纸牌?
可以强制执行这样的规定吗?
-(void) sort: (NSMutableArray <Card *>) deck {
}
自2015年起,Objective-C支持轻量级泛型,使用Xcode 7即可。
Xcode 7编译器会在类型不匹配时给出编译器警告。
例如,以下行会引发编译器警告,因为数组中的第二个对象导致了类型不匹配。该数组仅允许NSString
对象。
NSArray <NSString *> *myArray = [@"str2", @1, @"str2"];
-(void) sort: (NSMutableArray *) deck {
for(id obj in deck){
if(obj isKindOfClass:[A class]]){
//this is of right class
}
}
}
NSArray
上编写一个分类方法,对每个对象进行检查。BOOL allAreKindOfA = [array allObjectsAreKindOfClass:[A class]];
另一个选项可能是NSMutableArray
的子类,只接受特定的类。但要注意NSMutableArray和NSArray的子类说明,因为它们是类簇,所以不容易子类化。
注意:在我的其他答案中,我创建了一个NSMutableArray
子类,使用一个块来测试是否满足某个要求。如果你针对成员类进行测试,这将完全符合你的要求。使用第二个块进行错误处理。
从Xcode 7版本开始,苹果公司增加了对Objective-C泛型的支持。
NSArray <NSString *> *arrayOfStrings = @[@"a", @"b"];
NSDictionary <NSString *, NSDate *> *dictionaryOfDates = @{ @"a" : @1 };
NSArray <NSString *> *aarray = @[@"aa", @"bb", @1];
在我的 Xcode 7 beta 中没有产生任何错误。 - vikingosegundotemplate <typename T>
class t_typed_NSMutableArray {
public:
t_typed_NSMutableArray() : d_array([NSMutableArray new]) {}
~t_typed_NSMutableArray() { [d_array release]; }
/* ... */
T* operator[](const size_t& idx) {
T* const obj([this->d_array objectAtIndex:idx]);
assert([obj isKindOfClass:[T class]]);
return obj;
}
void addObject(T* const obj) {
assert([obj isKindOfClass:[T class]]);
[this->d_array addObject:obj];
}
private:
NSMutableArray * const d_array;
};
使用中:
t_typed_NSMutableArray<Card> array([self cards]); // < note this exact constructor is not defined
Card * firstCard = array[0]; // << ok
NSString * string = array[0]; // << warning
如果你使用类型安全的集合,那么在传递集合时也会进行重载检查,这样你就无法将 t_typed_NSArray<Card>
作为 t_typed_NSArray<NSURL>
进行传递。
受MonomorphicArray的启发,我想到了另一个主意:
创建一个NSMutableArray的子类,该子类接受两个块:
AddBlock可以测试特定类成员身份,例如
^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
}
FailBlock可以抛出异常、无声失败或将未通过测试的元素添加到另一个数组中。如果没有提供failBlock,则默认块会引发错误。
这些块将定义数组是充当通用数组还是过滤器。
我将为第二种情况给出完整的示例。
VSBlockTestedObjectArray.h
#import <Foundation/Foundation.h>
typedef BOOL(^AddBlock)(id element);
typedef void(^FailBlock)(id element);
@interface VSBlockTestedObjectArray : NSMutableArray
@property (nonatomic, copy, readonly) AddBlock testBlock;
@property (nonatomic, copy, readonly) FailBlock failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock Capacity:(NSUInteger)capacity;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock;
@end
VSBlockTestedObjectArray.m
#import "VSBlockTestedObjectArray.h"
@interface VSBlockTestedObjectArray ()
@property (nonatomic, retain) NSMutableArray *realArray;
-(void)errorWhileInitializing:(SEL)selector;
@end
@implementation VSBlockTestedObjectArray
@synthesize testBlock = _testBlock;
@synthesize failBlock = _failBlock;
@synthesize realArray = _realArray;
-(id)initWithCapacity:(NSUInteger)capacity
{
if (self = [super init]) {
_realArray = [[NSMutableArray alloc] initWithCapacity:capacity];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock
FailBlock:(FailBlock)failBlock
Capacity:(NSUInteger)capacity
{
self = [self initWithCapacity:capacity];
if (self) {
_testBlock = [testBlock copy];
_failBlock = [failBlock copy];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock
{
return [self initWithTestBlock:testBlock FailBlock:failBlock Capacity:0];
}
-(id)initWithTestBlock:(AddBlock)testBlock
{
return [self initWithTestBlock:testBlock FailBlock:^(id element) {
[NSException raise:@"NotSupportedElement" format:@"%@ faild the test and can't be add to this VSBlockTestedObjectArray", element];
} Capacity:0];
}
- (void)dealloc {
[_failBlock release];
[_testBlock release];
self.realArray = nil;
[super dealloc];
}
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if(self.testBlock(anObject))
[self.realArray insertObject:anObject atIndex:index];
else
self.failBlock(anObject);
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[self.realArray removeObjectAtIndex:index];
}
-(NSUInteger)count
{
return [self.realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [self.realArray objectAtIndex:index];
}
-(void)errorWhileInitializing:(SEL)selector
{
[NSException raise:@"NotSupportedInstantiation" format:@"not supported %@", NSStringFromSelector(selector)];
}
- (id)initWithArray:(NSArray *)anArray { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfFile:(NSString *)aPath{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfURL:(NSURL *)aURL{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(id)firstObj, ... { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { [self errorWhileInitializing:_cmd]; return nil;}
@end
使用方式如下:
VSBlockTestedObjectArray *stringArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
} FailBlock:^(id element) {
NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSString", element);
}];
VSBlockTestedObjectArray *numberArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSNumber class]];
} FailBlock:^(id element) {
NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSNumber", element);
}];
[stringArray addObject:@"test"];
[stringArray addObject:@"test1"];
[stringArray addObject:[NSNumber numberWithInt:9]];
[stringArray addObject:@"test2"];
[stringArray addObject:@"test3"];
[numberArray addObject:@"test"];
[numberArray addObject:@"test1"];
[numberArray addObject:[NSNumber numberWithInt:9]];
[numberArray addObject:@"test2"];
[numberArray addObject:@"test3"];
NSLog(@"%@", stringArray);
NSLog(@"%@", numberArray);
注意:此代码尚未进行全面测试。也许应该实现一些未实现的方法,以便在实际程序中使用。
https://github.com/tomersh/Objective-C-Generics
你最终将得到100%合法的Obj-C代码,看起来像这样:NSArray<CustomClass> anArray= ...
CustomClass a = anArray[0]; // works perfectly, and Xcode autocomplete works too!