Вы можете создать категорию с -addSomeClass: методом, позволяющим проверять статический тип во время компиляции (чтобы компилятор мог сообщить вам, если вы попытаетесь добавить объект, который, как он знает, является другим классом с помощью этого метода), но нет реального способа обеспечить это массив содержит только объекты данного класса.
В целом, похоже, что в Objective-C нет необходимости в таком ограничении. Я не думаю, что когда-либо слышал, чтобы опытный программист Cocoa хотел эту функцию. Единственные люди, которые кажутся программистами с других языков, все еще думают на этих языках. Если вам нужны только объекты данного класса в массиве, вставляйте туда только объекты этого класса. Если вы хотите проверить, что ваш код работает правильно, протестируйте его.
Я думаю, что «опытные программисты Cocoa» просто не знают, чего им не хватает - опыт работы с Java показывает, что переменные типа улучшают понимание кода и делают возможным больше рефакторингов.
tgdavies
11
Ну, поддержка Java Generics сама по себе сильно нарушена, потому что они не
добавляли
28
Должен согласиться с @tgdavies. Мне не хватает возможностей intellisense и рефакторинга, которые у меня были с C #. Когда мне нужна динамическая типизация, я могу получить ее в C # 4.0. Когда я хочу что-то сильно набирать, я могу и это. Я обнаружил, что для этих двух вещей есть время и место.
Steve
18
@charkrit Что такого особенного в Objective-C, что делает его «ненужным»? Считали ли вы это необходимым, когда использовали C #? Я слышал, как многие люди говорят, что вам это не нужно в Objective-C, но я думаю, что эти же люди думают, что вам это не нужно ни на каком языке, что делает это вопросом предпочтений / стиля, а не необходимости.
bacar
17
Разве это не о том, чтобы позволить вашему компилятору действительно помочь вам найти проблемы. Конечно, вы можете сказать: «Если вам нужны только объекты данного класса в массиве, вставляйте туда только объекты этого класса». Но если тесты - единственный способ добиться этого, вы окажетесь в невыгодном положении. Чем дальше от написания кода вы обнаружите проблему, тем дороже она будет стоить.
GreenKiwi
145
Это еще никто не вывесил, так что я сделаю это!
Теперь это официально поддерживается в Objective-C. Начиная с Xcode 7, вы можете использовать следующий синтаксис:
Важно отметить, что это только предупреждения компилятора, и технически вы все еще можете вставить любой объект в свой массив. Доступны сценарии, которые превращают все предупреждения в ошибки, которые могут помешать сборке.
Я здесь ленив, но почему это доступно только в XCode 7? Мы можем использовать их nonnullв XCode 6, и, насколько я помню, они были введены одновременно. Кроме того, зависит ли использование таких концепций от версии XCode или от версии iOS?
Guven
@Guven - возможность обнуления появилась в 6, вы правы, но дженерики ObjC не были представлены до Xcode 7.
Логан
Я почти уверен, что это зависит только от версии Xcode. Универсальные шаблоны являются только предупреждениями компилятора и не отображаются во время выполнения. Я почти уверен, что вы сможете компилировать все, что захотите.
Logan
2
@DeanKelly - Вы могли бы сделать это вот так: @property (nonatomic, strong) NSArray<id<SomeProtocol>>* protocolObjects; Выглядит немного неуклюже, но помогает!
Logan
1
@Logan, это не только набор скриптов, предотвращающих сборку в случае обнаружения какого-либо предупреждения. Xcode имеет прекрасный механизм под названием «Конфигурация». Проверьте это boredzo.org/blog/archives/2009-11-07/warnings
adnako
53
Это относительно частый вопрос для людей, переходящих от языков строгого типа (таких как C ++ или Java) к более слабо или динамически типизированным языкам, таким как Python, Ruby или Objective-C. В Objective-C большинство объектов наследуются от NSObject(типа id) (остальные наследуются от другого корневого класса, например, NSProxyи также могут иметь тип id), и любое сообщение может быть отправлено любому объекту. Конечно, отправка сообщения экземпляру, который он не распознает, может вызвать ошибку времени выполнения (а также вызовет предупреждение компилятора.с соответствующими флагами -W). Пока экземпляр отвечает на отправленное вами сообщение, вам может быть все равно, к какому классу он принадлежит. Это часто называют «уткой», потому что «если она крякает, как утка [т.е. отвечает на селектор], это утка [т.е. она может обрабатывать сообщение; кого волнует, какой это класс]».
Вы можете проверить, реагирует ли экземпляр на селектор во время выполнения с помощью -(BOOL)respondsToSelector:(SEL)selectorметода. Предполагая, что вы хотите вызвать метод для каждого экземпляра в массиве, но не уверены, что все экземпляры могут обработать сообщение (поэтому вы не можете просто использовать NSArray's -[NSArray makeObjectsPerformSelector:], что-то вроде этого будет работать:
for(id o in myArray){if([o respondsToSelector:@selector(myMethod)]){[o myMethod];}}
Если вы управляете исходным кодом для экземпляров, которые реализуют метод (ы), которые вы хотите вызвать, более распространенным подходом будет определение @protocolкласса, который содержит эти методы, и объявление того, что рассматриваемые классы реализуют этот протокол в своем объявлении. В этом случае a @protocolаналогичен интерфейсу Java или абстрактному базовому классу C ++. Затем вы можете проверить соответствие всему протоколу, а не ответ на каждый метод. В предыдущем примере это не имело бы большого значения, но если бы вы вызывали несколько методов, это могло бы упростить ситуацию. Тогда пример будет:
for(id o in myArray){if([o conformsToProtocol:@protocol(MyProtocol)]){[o myMethod];}}
предполагая MyProtocolобъявляет myMethod. Этот второй подход является предпочтительным, поскольку он более четко разъясняет цель кода, чем первый.
Часто один из этих подходов освобождает вас от заботы о том, все ли объекты в массиве относятся к заданному типу. Если вам все равно, стандартный подход динамического языка - это модульное тестирование, модульное тестирование, модульное тестирование. Поскольку регресс в этом требовании приведет к ошибке (вероятно, неисправимой) во время выполнения (а не во время компиляции), вам необходимо иметь тестовое покрытие для проверки поведения, чтобы вы не выпускали сбойный модуль на волю. В этом случае выполните операцию, которая изменяет массив, а затем убедитесь, что все экземпляры в массиве принадлежат данному классу. При надлежащем тестовом покрытии вам даже не потребуются дополнительные затраты времени выполнения на проверку идентичности экземпляра. У вас действительно хорошее покрытие модульных тестов, не так ли?
Модульное тестирование не заменяет достойную систему типов.
tba
8
Да, кому нужны инструменты, которые могут себе позволить типизированные массивы. Я уверен, что @BarryWark (и любой другой, кто прикоснулся к любой кодовой базе, которую ему нужно использовать, читать, понимать и поддерживать) имеет 100% покрытие кода. Однако я уверен, что вы не используете raw, idкроме случаев, когда это необходимо, как и кодеры Java не передают Objects. Почему нет? Не нужно, если у вас есть модульные тесты? Потому что он там и делает ваш код более удобным в сопровождении, как и типизированные массивы. Похоже, люди инвестировали в платформу, не желая уступать ни в чем, и поэтому придумывали причины, по которым это упущение на самом деле является преимуществом.
funkybro 07
"Утка печатает" ?? это весело! никогда не слышал этого раньше.
Джон Хенкель
11
Вы можете создать подкласс, NSMutableArrayчтобы обеспечить безопасность типов.
NSMutableArrayявляется кластером классов , поэтому создание подклассов нетривиально. В итоге я унаследовал NSArrayи перенаправил вызовы в массив внутри этого класса. В результате класс называется , ConcreteMutableArrayкоторый является легко подкласс. Вот что я придумал:
Приятно, но на данный момент в нем отсутствует строгая типизация за счет переопределения некоторых методов. На данный момент это только слабая типизация.
Cœur
7
Взгляните на https://github.com/tomersh/Objective-C-Generics , реализацию дженериков времени компиляции (реализованную препроцессором) для Objective-C. В этом сообщении в блоге есть хороший обзор. В основном вы получаете проверку во время компиляции (предупреждения или ошибки), но без штрафа во время выполнения для дженериков.
Возможным способом могло бы быть создание подкласса NSArray, но Apple не рекомендует этого делать. Проще дважды подумать о реальной потребности в типизированном массиве NSArray.
Проверка статического типа во время компиляции экономит время, редактирование становится еще лучше. Особенно полезно при написании библиотеки для длительного использования.
pinxue
0
Я создал подкласс NSArray, который использует объект NSArray в качестве поддерживающего ivar, чтобы избежать проблем с характером NSArray как кластера классов. Требуются блоки, чтобы принять или отклонить добавление объекта.
чтобы разрешить только объекты NSString, вы можете определить AddBlockкак
Вы можете определить, FailBlockчто делать, если элемент не прошел тест - корректно завершился неудачей для фильтрации, добавить его в другой массив или - это значение по умолчанию - вызвать исключение.
VSBlockTestedObjectArray*stringArray =[[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element){return[element isKindOfClass:[NSStringclass]];}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:[NSNumberclass]];}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);
Это просто пример кода, который никогда не использовался в реальных приложениях. для этого, вероятно, потребуется еще реализовать метод NSArray.
Если вы смешиваете C ++ и objective-c (то есть используя тип файла mm), вы можете принудительно ввести тип с помощью пары или кортежа. Например, в следующем методе вы можете создать объект C ++ типа std :: pair, преобразовать его в объект типа оболочки OC (оболочку std :: pair, которую необходимо определить), а затем передать ее некоторым другой метод OC, в котором вам нужно преобразовать объект OC обратно в объект C ++, чтобы использовать его. Метод OC принимает только тип оболочки OC, тем самым обеспечивая безопасность типа. Вы даже можете использовать кортеж, вариативный шаблон, список типов, чтобы использовать более продвинутые функции C ++ для обеспечения безопасности типов.
Ответы:
Вы можете создать категорию с
-addSomeClass:
методом, позволяющим проверять статический тип во время компиляции (чтобы компилятор мог сообщить вам, если вы попытаетесь добавить объект, который, как он знает, является другим классом с помощью этого метода), но нет реального способа обеспечить это массив содержит только объекты данного класса.В целом, похоже, что в Objective-C нет необходимости в таком ограничении. Я не думаю, что когда-либо слышал, чтобы опытный программист Cocoa хотел эту функцию. Единственные люди, которые кажутся программистами с других языков, все еще думают на этих языках. Если вам нужны только объекты данного класса в массиве, вставляйте туда только объекты этого класса. Если вы хотите проверить, что ваш код работает правильно, протестируйте его.
источник
Это еще никто не вывесил, так что я сделаю это!
Теперь это официально поддерживается в Objective-C. Начиная с Xcode 7, вы можете использовать следующий синтаксис:
Заметка
Важно отметить, что это только предупреждения компилятора, и технически вы все еще можете вставить любой объект в свой массив. Доступны сценарии, которые превращают все предупреждения в ошибки, которые могут помешать сборке.
источник
nonnull
в XCode 6, и, насколько я помню, они были введены одновременно. Кроме того, зависит ли использование таких концепций от версии XCode или от версии iOS?@property (nonatomic, strong) NSArray<id<SomeProtocol>>* protocolObjects;
Выглядит немного неуклюже, но помогает!Это относительно частый вопрос для людей, переходящих от языков строгого типа (таких как C ++ или Java) к более слабо или динамически типизированным языкам, таким как Python, Ruby или Objective-C. В Objective-C большинство объектов наследуются от
NSObject
(типаid
) (остальные наследуются от другого корневого класса, например,NSProxy
и также могут иметь типid
), и любое сообщение может быть отправлено любому объекту. Конечно, отправка сообщения экземпляру, который он не распознает, может вызвать ошибку времени выполнения (а также вызовет предупреждение компилятора.с соответствующими флагами -W). Пока экземпляр отвечает на отправленное вами сообщение, вам может быть все равно, к какому классу он принадлежит. Это часто называют «уткой», потому что «если она крякает, как утка [т.е. отвечает на селектор], это утка [т.е. она может обрабатывать сообщение; кого волнует, какой это класс]».Вы можете проверить, реагирует ли экземпляр на селектор во время выполнения с помощью
-(BOOL)respondsToSelector:(SEL)selector
метода. Предполагая, что вы хотите вызвать метод для каждого экземпляра в массиве, но не уверены, что все экземпляры могут обработать сообщение (поэтому вы не можете просто использоватьNSArray
's-[NSArray makeObjectsPerformSelector:]
, что-то вроде этого будет работать:Если вы управляете исходным кодом для экземпляров, которые реализуют метод (ы), которые вы хотите вызвать, более распространенным подходом будет определение
@protocol
класса, который содержит эти методы, и объявление того, что рассматриваемые классы реализуют этот протокол в своем объявлении. В этом случае a@protocol
аналогичен интерфейсу Java или абстрактному базовому классу C ++. Затем вы можете проверить соответствие всему протоколу, а не ответ на каждый метод. В предыдущем примере это не имело бы большого значения, но если бы вы вызывали несколько методов, это могло бы упростить ситуацию. Тогда пример будет:предполагая
MyProtocol
объявляетmyMethod
. Этот второй подход является предпочтительным, поскольку он более четко разъясняет цель кода, чем первый.Часто один из этих подходов освобождает вас от заботы о том, все ли объекты в массиве относятся к заданному типу. Если вам все равно, стандартный подход динамического языка - это модульное тестирование, модульное тестирование, модульное тестирование. Поскольку регресс в этом требовании приведет к ошибке (вероятно, неисправимой) во время выполнения (а не во время компиляции), вам необходимо иметь тестовое покрытие для проверки поведения, чтобы вы не выпускали сбойный модуль на волю. В этом случае выполните операцию, которая изменяет массив, а затем убедитесь, что все экземпляры в массиве принадлежат данному классу. При надлежащем тестовом покрытии вам даже не потребуются дополнительные затраты времени выполнения на проверку идентичности экземпляра. У вас действительно хорошее покрытие модульных тестов, не так ли?
источник
id
кроме случаев, когда это необходимо, как и кодеры Java не передаютObject
s. Почему нет? Не нужно, если у вас есть модульные тесты? Потому что он там и делает ваш код более удобным в сопровождении, как и типизированные массивы. Похоже, люди инвестировали в платформу, не желая уступать ни в чем, и поэтому придумывали причины, по которым это упущение на самом деле является преимуществом.Вы можете создать подкласс,
NSMutableArray
чтобы обеспечить безопасность типов.NSMutableArray
является кластером классов , поэтому создание подклассов нетривиально. В итоге я унаследовалNSArray
и перенаправил вызовы в массив внутри этого класса. В результате класс называется ,ConcreteMutableArray
который является легко подкласс. Вот что я придумал:Обновление: ознакомьтесь с этим сообщением в блоге Майка Эша о создании подклассов кластера классов.
Включите эти файлы в свой проект, а затем сгенерируйте любые типы, которые захотите, используя макросы:
MyArrayTypes.h
MyArrayTypes.m
Использование:
другие мысли
NSArray
для поддержки сериализации / десериализацииВ зависимости от вашего вкуса вы можете переопределить / скрыть общие методы, такие как
- (void) addObject:(id)anObject
источник
Взгляните на https://github.com/tomersh/Objective-C-Generics , реализацию дженериков времени компиляции (реализованную препроцессором) для Objective-C. В этом сообщении в блоге есть хороший обзор. В основном вы получаете проверку во время компиляции (предупреждения или ошибки), но без штрафа во время выполнения для дженериков.
источник
Этот проект Github реализует именно эту функциональность.
Затем вы можете использовать
<>
скобки, как в C #.Из их примеров:
источник
Возможным способом могло бы быть создание подкласса NSArray, но Apple не рекомендует этого делать. Проще дважды подумать о реальной потребности в типизированном массиве NSArray.
источник
Я создал подкласс NSArray, который использует объект NSArray в качестве поддерживающего ivar, чтобы избежать проблем с характером NSArray как кластера классов. Требуются блоки, чтобы принять или отклонить добавление объекта.
чтобы разрешить только объекты NSString, вы можете определить
AddBlock
какВы можете определить,
FailBlock
что делать, если элемент не прошел тест - корректно завершился неудачей для фильтрации, добавить его в другой массив или - это значение по умолчанию - вызвать исключение.VSBlockTestedObjectArray.h
VSBlockTestedObjectArray.m
Используйте это как:
Это просто пример кода, который никогда не использовался в реальных приложениях. для этого, вероятно, потребуется еще реализовать метод NSArray.
источник
Если вы смешиваете C ++ и objective-c (то есть используя тип файла mm), вы можете принудительно ввести тип с помощью пары или кортежа. Например, в следующем методе вы можете создать объект C ++ типа std :: pair, преобразовать его в объект типа оболочки OC (оболочку std :: pair, которую необходимо определить), а затем передать ее некоторым другой метод OC, в котором вам нужно преобразовать объект OC обратно в объект C ++, чтобы использовать его. Метод OC принимает только тип оболочки OC, тем самым обеспечивая безопасность типа. Вы даже можете использовать кортеж, вариативный шаблон, список типов, чтобы использовать более продвинутые функции C ++ для обеспечения безопасности типов.
источник
мои два цента, чтобы быть немного «чище»:
используйте typedefs:
в коде мы можем:
источник
2020, прямой ответ. Так уж вышло, что мне нужен изменяемый массив с типом
NSString
.Синтаксис:
Пример:
источник