Поддерживает ли Objective-C Generics? - PullRequest
12 голосов
/ 19 ноября 2011

Интересно, предлагает ли Objective-C какую-либо поддержку для дженериков?

Например, рассмотрим метод:

-(void) sort: (NSMutableArray *) deck {
}

Можно ли как-нибудь заставить ее работать только с колодой карт?
Возможно ли применить что-то подобное?

-(void) sort: (NSMutableArray <Card *>) deck {
}

Ответы [ 6 ]

11 голосов
/ 10 июня 2015

Objective C теперь поддерживает Generics с XCode 7.

Компилятор XCode 7 выдаст вам предупреждение компилятора в случае несоответствия типов.

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

NSArray <NSString *> *myArray = [@"str2", @1, @"str2"];
6 голосов
/ 19 ноября 2011

Вы можете использовать инструменты для самоанализа , предлагаемые средой выполнения target-c.

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

-(void) sort: (NSMutableArray *) deck {
    for(id obj in deck){
        if(obj isKindOfClass:[A class]]){
            //this is of right class
        }
    }
}

Вы можете написать метод Category в NSArray, который проверяет это для каждого объекта.

BOOL allAreKindOfA = [array allObjectsAreKindOfClass:[A class]];

Обычно вам это не нужно очень часто, так как вы знаете, что положили в коллекцию.

Если вам нужно проверить тип или способность объекта в массиве, это может быть индикатором того, что ваша архитектура нарушена


Другим вариантом может быть подкласс NSMutableArray, который принимает только определенные классы. Но имейте в виду примечания по подклассам для NSMutableArray и NSArray , так как они являются кластерами классов и поэтому их нелегко подклассировать.

Примечание: В моем другом ответе я создал NSMutableArray подкласс, который использует блок для проверки, если определенное требование выполнено. Если вы протестируете против членства в классе, это будет делать именно то, что вы хотите. Используйте второй блок для обработки ошибок.

5 голосов
/ 09 июня 2015

Начиная с выпуска Xcode 7, Apple добавила поддержку обобщений Objective-C.

NSArray <NSString *> *arrayOfStrings = @[@"a", @"b"];

NSDictionary <NSString *, NSDate *> *dictionaryOfDates = @{ @"a" : @1 };
4 голосов
/ 01 ноября 2014

Существует простой и эффективный способ сделать это (я использую его в проектах уже пару лет). К сожалению, кто-то удалил ответ, и мои попытки восстановить его были отклонены. Здесь снова:

Вы можете повторно реализовать урезанную версию шаблонов 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, с автозаполнением и т. Д.

4 голосов
/ 20 ноября 2011

Вдохновленный 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.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);

Примечание: Этот код не полностью протестирован.Вероятно, некоторые из неосуществленных методов должны быть реализованы для использования в реальных программах.

4 голосов
/ 19 ноября 2011

Не напрямую, нет. Есть несколько способов его симуляции, но для этого требуется много кода-обертки, стандартного кода и накладных расходов времени выполнения. Я просто переключаюсь на Objective-C ++ и использую шаблоны C ++, когда я хочу или нуждаюсь в надлежащих обобщениях.

Так что, если вы хотите ввести безопасность типов / проверки в 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>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...