Есть ли в Objective-C строго типизированные коллекции?

142

Я новичок в программировании Mac / iPhone и Objective-C. В C # и Java есть «дженерики», классы коллекций, члены которых могут быть только объявленного типа. Например, в C #

Dictionary<int, MyCustomObject>

могут содержать только ключи, которые являются целыми числами, и значениями типа MyCustomObject. Есть ли аналогичный механизм в Objective-C?

Богатый
источник
Я только начинаю узнавать об ObjC. Возможно, вы сможете использовать ObjC ++ для выполнения тяжелой работы?
Изготовитель игрушек
Возможно, вас заинтересуют ответы на этот вопрос: есть ли способ принудительно ввести тип в NSArray, NSMutableArray и т. Д.? . Приводятся аргументы, почему это не обычная практика в Objective-C / Cocoa.
mouviciel
2
ObjC ++ на самом деле не язык ... просто больше способ сослаться на способность ObjC обрабатывать встроенный C ++ точно так же, как и C. Вы не должны делать этого, если только вам не нужно (например, если вам нужно использовать стороннюю библиотеку, написанную на C ++).
Marc W
Практически точная копия stackoverflow.com/questions/649483/…
Барри Уорк,
@ Mark W - «не надо этого делать», почему бы и нет? Я использовал ObjC ++, и он отлично работает. Я могу использовать #import <map> и @property std :: map <int, NSString *> myDict; Я могу использовать полный API Какао И иметь строго типизированные коллекции. Я не вижу недостатков.
Джон Хенкель

Ответы:

212

В Xcode 7 Apple представила «Облегченные обобщения» для Objective-C. В Objective-C они будут генерировать предупреждения компилятора, если есть несоответствие типа.

NSArray<NSString*>* arr = @[@"str"];

NSString* string = [arr objectAtIndex:0];
NSNumber* number = [arr objectAtIndex:0]; // Warning: Incompatible pointer types initializing 'NSNumber *' with an expression of type 'NSString *'

А в коде Swift они выдадут ошибку компилятора:

var str: String = arr[0]
var num: Int = arr[0] //Error 'String' is not convertible to 'Int'

Легкие универсальные шаблоны предназначены для использования с NSArray, NSDictionary и NSSet, но вы также можете добавить их в свои собственные классы:

@interface GenericsTest<__covariant T> : NSObject

-(void)genericMethod:(T)object;

@end

@implementation GenericsTest

-(void)genericMethod:(id)object {}

@end

Objective-C будет вести себя так же, как раньше, с предупреждениями компилятора.

GenericsTest<NSString*>* test = [GenericsTest new];

[test genericMethod:@"string"];
[test genericMethod:@1]; // Warning: Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'

но Swift полностью игнорирует общую информацию. (Больше не так в Swift 3+.)

var test = GenericsTest<String>() //Error: Cannot specialize non-generic type 'GenericsTest'

Помимо этих классов коллекций Foundation, Swift игнорирует облегченные универсальные шаблоны Objective-C. Любые другие типы, использующие облегченные универсальные шаблоны, импортируются в Swift, как если бы они не были параметризованы.

Взаимодействие с API Objective-C

Коннор
источник
Поскольку у меня есть вопрос о обобщениях и типах, возвращаемых в методах, я задал свой вопрос в другом потоке, чтобы все было понятно: stackoverflow.com/questions/30828076/…
lvp
2
@rizzes. Да, это только что представили.
Коннор
Одно предостережение заключается в том, что Swift не полностью игнорирует аннотации типов в вашем универсальном классе ObjC. Если вы укажете ограничения, например MyClass <Foo: id<Bar>>, ваш код Swift будет предполагать, что значения являются типом вашего ограничения, что дает вам то, с чем можно работать. Однако специализированные подклассы MyClassбудут игнорировать свои специализированные типы (фактически, они будут рассматриваться как универсальные MyClass). См. Github.com/bgerstle/LightweightGenericsExample
Брайан Герстл
Так компилируется ли это для операционных систем 10.10, 10.9 и более ранних версий?
p0lAris
Это должно быть сделано до тех пор, пока вы установили цель развертывания для их поддержки
Коннор,
91

Этот ответ устарел, но сохраняет историческую ценность. Что касается Xcode 7, ответ Коннора от 8 июня 2015 года более точен.


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

Objective-C имеет динамическую типизацию в качестве функции, что означает, что среда выполнения не заботится о типе объекта, поскольку все объекты могут получать сообщения. Когда вы добавляете объект во встроенную коллекцию, они рассматриваются как типовые id. Но не волнуйтесь, просто отправляйте сообщения этим объектам как обычно; он будет работать нормально (если, конечно, один или несколько объектов в коллекции не отвечают на отправляемое вами сообщение) .

Обобщения необходимы в таких языках, как Java и C #, потому что они являются сильными, статически типизированными языками. Совершенно другая игра, чем функция динамической печати в Objective-C.

Марк В
источник
88
Я не согласен с тем, что «не волнуйтесь, просто отправляйте сообщения этим объектам». Если вы поместите в коллекцию объекты неправильного типа, которые не отвечают на эти сообщения, это приведет к ошибкам выполнения. Использование дженериков на других языках позволяет избежать этой проблемы с проверками времени компиляции.
henning77 04
8
@ henning77 Да, но Objective-C более динамичный язык, чем эти языки. Если вам нужна строгая безопасность типов, используйте эти языки.
Раффи Хатчадурян
36
Я также не согласен с философией «не беспокойся» - например, если вы вытащите первый элемент из массива NSArray и передадите его в NSNumber, но этот элемент на самом деле был NSString, вы облажались ...
jjxtra
13
@RaffiKhatchadourian - не лучший выбор, если вы пишете приложение для iOS. Если бы было просто написать приложение на Java и получить все преимущества написания нативного приложения, поверьте мне: я бы стал.
ericsoco
11
Самая большая жалоба, которую я имею по этому поводу, связана не с динамическими языками и проверкой времени компиляции, а с простым общением с разработчиками. Я не могу просто смотреть на объявление свойства и знать, какой тип объектов он собирается вернуть, если это где-то не задокументировано.
devios1
11

Нет, но для большей ясности вы можете прокомментировать его, указав тип объекта, который вы хотите сохранить. Я видел это несколько раз, когда вам нужно написать что-то на Java 1.4 в наши дни) например:

NSMutableArray* /*<TypeA>*/ arrayName = ....

или

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
Марк Роудс
источник
Я думаю, это хороший способ документировать это на случай, если кто-то еще прочитает ваш код. В любом случае имя переменной должно быть максимально ясным, чтобы знать, какие объекты она содержит.
htafoya
6

В Objective-C нет дженериков.

Из Документов

Массивы - это упорядоченные коллекции объектов. Какао предоставляет несколько классов массивов, NSArray, NSMutableArray (подкласс NSArray) и NSPointerArray.

Мэтью Вайнс
источник
Ссылка на документ в ответе не работает - «Извините, эта страница не найдена» .
Pang
6

Это было выпущено в Xcode 7 (наконец-то!)

Обратите внимание, что в коде Objective C это просто проверка во время компиляции; не будет ошибок времени выполнения только из-за помещения неправильного типа в коллекцию или присвоения типизированному свойству.

Объявляем:

@interface FooClass <T> : NSObject
@property (nonatomic) T prop;
@end

Использование:

FooClass<NSString *> *foo = [[FooClass alloc] init];
NSArray<FooClass<NSString *> *> *fooAry = [NSArray array];

Будьте осторожны *с этими s.

Кевин
источник
4

Общие NSArrays могут быть реализованы путем создания подклассов NSArrayи переопределения всех предоставленных методов с помощью более ограничительных. Например,

- (id)objectAtIndex:(NSUInteger)index

пришлось бы переопределить в

@interface NSStringArray : NSArray

в качестве

- (NSString *)objectAtIndex:(NSUInteger)index

чтобы NSArray содержал только NSStrings.

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

Это можно автоматизировать и свести к двум операторам, что приближает его к языкам, поддерживающим дженерики. Я создал автоматизацию с помощью WMGenericCollection , где шаблоны предоставляются как макросы препроцессора C.

После импорта файла заголовка, содержащего макрос, вы можете создать общий массив NSArray с двумя операторами: один для интерфейса, а другой - для реализации. Вам нужно только указать тип данных, который вы хотите сохранить, и имена для ваших подклассов. WMGenericCollection предоставляет такие шаблоны NSArray, NSDictionaryи NSSet, как и их коллеги изменяемые.

Пример: List<int>может быть реализован с помощью настраиваемого класса с именем NumberArray, который создается с помощью следующего оператора:

WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class
                         // generated class names
                         NumberArray, MutableNumberArray)

После того, как вы создали NumberArray, вы можете использовать его везде в своем проекте. В нем отсутствует синтаксис <int>, но вы можете выбрать свою собственную схему именования, чтобы пометить их как классы как шаблоны.

wm
источник
обратите внимание, что то же самое существует в CoreLib: github.com/core-code/CoreLib/blob/master/CoreLib/CoreLib.h#L105
user1259710
2

Теперь мечты сбываются - с сегодняшнего дня в Objective-C есть Generics (спасибо, WWDC). Это не шутка - на официальной странице Swift:

Новые функции синтаксиса позволяют писать более выразительный код, улучшая согласованность языка. В SDK используются новые функции Objective-C, такие как обобщения и аннотация, допускающая значение NULL, чтобы сделать код Swift еще чище и безопаснее. Вот лишь некоторые из улучшений Swift 2.0.

И изображение, подтверждающее это:Дженерики Objective-C

htzfun
источник
2

Просто хочу прыгнуть сюда. Я написал сообщение в блоге здесь о дженериков.

Я хочу внести свой вклад в то, что Generics можно добавлять в любой класс. , а не только в классы коллекции, как указывает Apple.

Затем я успешно добавил к множеству классов, поскольку они работают точно так же, как коллекции Apple. т.е. проверка времени компиляции, завершение кода, включение удаления приведения типов и т. д.

Наслаждаться.

дрекка
источник
-2

Классы коллекций, предоставляемые фреймворками Apple и GNUStep, являются полуобобщенными в том смысле, что они предполагают, что им предоставлены объекты, некоторые из которых можно сортировать, а некоторые отвечают на определенные сообщения. Для примитивов, таких как числа с плавающей запятой, целые числа и т. Д., Вся структура массивов C не повреждена и может использоваться, и для них есть специальные объекты-оболочки для использования в общих классах коллекций (например, NSNumber). Кроме того, класс Collection может быть разделен на подклассы (или специально модифицирован с помощью категорий), чтобы принимать объекты любого типа, но вы должны написать весь код обработки типов самостоятельно. Сообщения могут быть отправлены любому объекту, но должны возвращать null, если это не подходит для объекта, или сообщение должно быть отправлено соответствующему объекту. Ошибки истинного типа следует обнаруживать во время компиляции, а не во время выполнения. Во время выполнения они должны обрабатываться или игнорироваться. Наконец, Objc предоставляет средства отражения во время выполнения для обработки сложных случаев, а ответ на сообщение, конкретный тип и службы могут быть проверены на объекте перед отправкой сообщения или помещением в несоответствующую коллекцию. Помните, что разные библиотеки и фреймворки принимают разные соглашения относительно того, как их объекты ведут себя при отправке сообщений, для которых у них нет кодовых ответов, поэтому RTFM. За исключением игрушечных программ и отладочных сборок, большинство программ не должны аварийно завершаться, если они действительно не облажаются и не попытаются записать неверные данные в память или на диск, выполнить недопустимые операции (например, разделить на ноль, но это тоже можно поймать) или получить запрещает системные ресурсы. Динамизм и время выполнения Objective-C позволяют изящно выходить из строя и должны быть встроены в ваш код. (ПОДСКАЗКА) если у вас проблемы с универсальностью ваших функций, попробуй некоторую конкретность. Напишите функции определенными типами и позвольте среде выполнения выбирать (вот почему они называются селекторами!) Соответствующую функцию-член во время выполнения.

Example:
    -(id) sort (id) obj;  // too generic. catches all.
     // better
    -(id) sort: (EasilySortableCollection*) esc;
    -(id) sort: (HardToSortCollection*) hsc; 
    ...
    [Sorter  sort: MyEasyColl];
    [Sorter  sort: MyHardColl];
Крис Рид
источник