Есть ли способ принудительного ввода текста в 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]; }