Есть ли способ принудительного ввода текста в NSArray, NSMutableArray и т. Д.?

Можно ли создать экземпляр NSMutableArray где все элементы имеют тип SomeClass ?

    Вы можете создать категорию с помощью метода -addSomeClass: метод, позволяющий проверять статический тип времени компиляции (поэтому компилятор может сообщить вам, пытаетесь ли вы добавить объект, который, как он знает, является другим classом с помощью этого метода), но нет реального способа для обеспечения того, чтобы массив содержал только объекты данного classа.

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

    Пока никто этого не сказал, так что я сделаю это!

    Это официально поддерживается в Objective-C. Начиная с Xcode 7, вы можете использовать следующий синтаксис:

     NSArray *myArray = @[[MyClass new], [MyClass new]]; 

    Заметка

    Важно отметить, что это только предупреждения компилятора, и вы можете технически по-прежнему вставлять любой объект в свой массив. Доступны сценарии, которые превращают все предупреждения в ошибки, которые препятствовали бы созданию.

    Это относительно распространенный вопрос для людей, переходящих с сильно родственных языков (например, C ++ или Java) на более слабо или динамически типизированные языки, такие как Python, Ruby или Objective-C. В Objective-C большинство объектов наследуется от NSObject ( id типа) (остальные наследуют от другого корневого classа, такого как NSProxy а также могут быть id типа), и любое сообщение может быть отправлено любому объекту. Конечно, отправка сообщения экземпляру, который он не распознает, может привести к ошибке выполнения (а также вызовет предупреждение компилятора с соответствующими флагами -W). Пока экземпляр отвечает на отправляемое сообщение, вам может не понравиться, к какому classу он принадлежит. Это часто упоминается как «утиная печать», потому что «если он сбрасывается, как утка (т.е. отвечает на селектор), это утка [то есть, он может обрабатывать сообщение, кто заботится о том, какой class он»).

    Вы можете проверить, реагирует ли экземпляр на селектор во время выполнения с помощью метода -(BOOL)respondsToSelector:(SEL)selector метод -(BOOL)respondsToSelector:(SEL)selector . Предполагая, что вы хотите вызвать метод для каждого экземпляра в массиве, но не уверены, что все экземпляры могут обрабатывать сообщение (поэтому вы не можете просто использовать NSArray -[NSArray makeObjectsPerformSelector:] , что-то вроде этого будет работать:

     for(id o in myArray) { if([o respondsToSelector:@selector(myMethod)]) { [o myMethod]; } } 

    Если вы управляете исходным кодом для экземпляров, которые реализуют метод (ы), который вы хотите вызвать, более общим подходом было бы определить @protocol который содержит эти методы, и объявить, что рассматриваемые classы реализуют этот протокол в своей декларации. В этом использовании @protocol аналогичен интерфейсу Java или базовому classу C ++. Затем вы можете проверить соответствие для всего протокола, а не отвечать на каждый метод. В предыдущем примере это не имело бы большого значения, но если вы вызывали несколько методов, это могло бы упростить ситуацию. Примером может служить следующий пример:

     for(id o in myArray) { if([o conformsToProtocol:@protocol(MyProtocol)]) { [o myMethod]; } } 

    предполагая, что MyProtocol объявляет myMethod . Этот второй подход одобрен, поскольку он разъясняет цель кода больше, чем первый.

    Часто один из этих подходов освобождает вас от заботы о том, являются ли все объекты в массиве заданного типа. Если вы по-прежнему заботитесь, стандартным подходом к динамическому языку является модульный тест, модульный тест, модульный тест. Поскольку регрессия в этом требовании приведет к ошибке (вероятно, невосстановимой) времени выполнения (не время компиляции), вам необходимо иметь покрытие тестирования, чтобы проверить поведение, чтобы вы не выпускали crasher в дикую природу. В этом случае выполните операцию, которая модифицирует массив, а затем убедитесь, что все экземпляры массива принадлежат данному classу. При правильном тестировании вы даже не нуждаетесь в дополнительных накладных расходах времени выполнения проверки подлинности экземпляра. У вас действительно хорошее тестирование тестового пакета, не так ли?

    Вы можете подclassифицировать NSMutableArray для обеспечения безопасности типов.

    NSMutableArray – кластер classов , поэтому подclassификация не является тривиальной. Я закончил наследование от NSArray и перенаправил вызовы в массив внутри этого classа. Результатом является class ConcreteMutableArray который легко подclassы. Вот что я придумал:

    • CustomArray.h
    • CustomArray.m

    Обновление: проверьте этот пост в блоге от Майка Эша в подclassе кластера classов.

    Включите эти файлы в свой проект, а затем сгенерируйте любые типы с помощью макросов:

    MyArrayTypes.h

     CUSTOM_ARRAY_INTERFACE(NSString) CUSTOM_ARRAY_INTERFACE(User) 

    MyArrayTypes.m

     CUSTOM_ARRAY_IMPLEMENTATION(NSString) CUSTOM_ARRAY_IMPLEMENTATION(User) 

    Применение:

     NSStringArray* strings = [NSStringArray array]; [strings add:@"Hello"]; NSString* str = [strings get:0]; [strings add:[User new]]; //compiler error User* user = [strings get:0]; //compiler error 

    другие мысли

    • Он наследует от NSArray поддержку сериализации / десериализации
    • В зависимости от вашего вкуса вы можете переопределить / скрыть общие методы, например

      - (void) addObject:(id)anObject

    Посмотрите на https://github.com/tomersh/Objective-C-Generics , реализацию генерируемых обобщенных данных (для препроцессора) для Objective-C. Это сообщение в блоге имеет хороший обзор. В основном вы получаете проверку времени компиляции (предупреждения или ошибки), но не соблюдение времени исполнения для дженериков.

    Этот проект Github реализует именно эту функциональность.

    Затем вы можете использовать скобки <> , как и на C #.

    Из их примеров:

     NSArray* classArray = [NSArray array]; NSString *name = [classArray lastObject].name; // No cast needed 

    Возможным способом может быть подclassификация NSArray, но Apple рекомендует не делать этого. Проще думать дважды о фактической потребности в типизированном NSArray.

    Я создал подclass NSArray, который использует объект NSArray в качестве поддержки ivar, чтобы избежать проблем с кластерным характером NSArray. Он принимает блоки для принятия или отклонения добавления объекта.

    чтобы разрешать объекты NSString, вы можете определить AddBlock как

     ^BOOL(id element) { return [element isKindOfClass:[NSString class]]; } 

    Вы можете определить FailBlock чтобы решить, что делать, если элемент не прошел тест – изящно FailBlock фильтрацию, добавляет его в другой массив или – это по умолчанию – вызывает исключение.

    VSBlockTestedObjectArray.h

     #import  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 , #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 , #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); 

    Это всего лишь примерный код и никогда не использовался в реальном мире. для этого, вероятно, необходим метод mor NSArray.

    Если вы смешиваете c ++ и object-c (т. Е. Используете тип файла mm), вы можете принудительно вводить ввод с использованием пары или кортежа. Например, в следующем методе вы можете создать объект C ++ типа std :: pair, преобразовать его в объект типа обертки OC (обертка std :: pair, которую вы должны определить), а затем передать его некоторому другой метод OC, в котором вам нужно преобразовать объект OC обратно в объект C ++, чтобы его использовать. Метод OC принимает только тип обертки OC, обеспечивая тем самым безопасность типов. Вы даже можете использовать кортеж, вариационный шаблон, список типов, чтобы использовать более продвинутые функции C ++ для облегчения безопасности типов.

     - (void) tableView:(UITableView*) tableView didSelectRowAtIndexPath:(NSIndexPath*) indexPath { std::pair tableRow(tableView, indexPath); ObjCTableRowWrapper* oCTableRow = [[[ObjCTableRowWrapper alloc] initWithTableRow:tableRow] autorelease]; [self performSelector:@selector(selectRow:) withObject:oCTableRow]; } 
    Interesting Posts

    Компилировать код с помощью JavaFX 2.0 (используя командную строку)

    Неверная длительность звука с помощью ffmpeg

    Как программно перечислять тип enums в TypeScript 0.9.5?

    Что было бы хорошим гигабитным маршрутизатором для дома с Windows Home Server?

    У пользователя базы данных HSQL отсутствует привилегия или объект не найдена ошибка

    Как преобразовать struct System.Byte byte в объект System.IO.Stream в C #?

    Удалите менеджер окон Gnome и установите диспетчер окон Xfce

    Запрос LINQ, чтобы найти элементы в списке, содержащиеся в другом списке

    Вставка в vim через экран

    Стоит ли тратить время на обучение Emacs?

    Могу ли я получить список подключенных клиентов на openvpn (например, «список hamachi»?)

    Android AlarmManager не работает на некоторых устройствах, когда приложение закрыто

    xmllint не может правильно запросить с помощью xpath

    Несколько пользовательских моделей с Ruby On Rails и разработайте отдельные маршруты регистрации, но один общий логин

    Создание PDF-документов

    Давайте будем гением компьютера.