Поддерживает ли Objective-C дженерики?
интересно, предлагает ли Objective-C какую-либо поддержку дженериков?
например, рассмотрим способ:
-(void) sort: (NSMutableArray *) deck {
}
есть ли способ для меня сделать это только с колодой карт?
Возможно ли что-то подобное принудить?
-(void) sort: (NSMutableArray <Card *>) deck {
}
6 ответов
можно использовать интроспекция инструменты предложенные объективные-c времени выполнения.
в основном, это означает, что вы можете проверить, являются ли все объекты в массиве класс (класс A или один его подкласс) или a член класса (класс A), или если объекты соответствует протоколу или отвечает селектору (присутствует определенный метод).
-(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
принимать только определенные классы. Но имейте в виду подклассы notes для NSMutableArray и NSArray, поскольку это кластеры классов и, следовательно, нелегко подкласс.
Примечание: в своем другого ответа Я создал NSMutableArray
подкласс, который использует блок для тестирования, если выполняется определенное требование. Если вы протестируете против членства в классе, это сделает именно то, что вы хотите. Используйте второй блок для обработки ошибок.
Objective C теперь поддерживает дженерики с XCode 7.
компилятор XCode 7 выдаст вам предупреждение компилятора, если есть несоответствие типа.
например, следующая строка вызовет предупреждение компилятора, поскольку второй объект в массиве вызывает несоответствие типов. Массив позволяет только NSString
объекты.
NSArray <NSString *> *myArray = [@"str2", @1, @"str2"];
начиная с выпуска Xcode 7, Apple добавила поддержку дженериков Objective-C.
NSArray <NSString *> *arrayOfStrings = @[@"a", @"b"];
NSDictionary <NSString *, NSDate *> *dictionaryOfDates = @{ @"a" : @1 };
вдохновленный MonomorphicArray мне пришла в голову еще одна мысль:
создайте подкласс на NSMutableArray, который занимает два блока:
- AddBlock-блок, который тестирует, если одно или несколько требований заполнены и добавляет объект только, если он проходит тест
- FailBlock-блок, который определяет, что произойдет, если тест не удался.
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.м
#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);
Примечание: этот код не полностью протестирована. Вероятно, некоторые из нереализованных методов должны быть реализованы для использования в реальных программах.
не напрямую, нет. Есть несколько способов имитировать его, но для этого требуется много кода оболочки, шаблонного кода и накладных расходов во время выполнения. Я просто переключаюсь на Objective-C++ и использую шаблоны C++, когда мне нужны правильные дженерики.
поэтому, если вы хотите ввести typesafety / checks в NSArray, вы можете подойти к нему, используя что-то вроде этого:
template <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>
.
есть простой и эффективный способ сделать это (я использую его в проектах уже пару лет). К сожалению, кто-то удалил ответ, и мои попытки восстановить его были отклонены. Вот опять:
вы можете повторно реализовать сокращенную версию шаблонов C++ в Obj-C, потому что Obj-C инкапсулирует все C (а шаблоны C++ - это c-макросы с улучшенной поддержкой компилятора/отладчика):
Это нужно сделать только один раз, используя один заголовок файл. Кто-то сделал это за вас:
https://github.com/tomersh/Objective-C-Generics
вы получаете 100% юридический код Obj-C, который выглядит следующим образом:
NSArray<CustomClass> anArray= ...
CustomClass a = anArray[0]; // works perfectly, and Xcode autocomplete works too!
все это отлично работает в XCode, с автозаполнением и т. д.