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

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

Dictionary<int, MyCustomObject>

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


person Rich    schedule 11.05.2009    source источник
comment
Я только начинаю узнавать об ObjC. Возможно, вы сможете использовать ObjC ++ для выполнения тяжелой работы?   -  person Toybuilder    schedule 11.05.2009
comment
Возможно, вас заинтересуют ответы на этот вопрос: Есть ли способ принудительно ввести тип в NSArray, NSMutableArray и т. Д.?. Приводятся аргументы, почему это не обычная практика в Objective-C / Cocoa.   -  person mouviciel    schedule 11.05.2009
comment
ObjC ++ на самом деле не язык ... просто способ сослаться на способность ObjC обрабатывать встроенный C ++ точно так же, как и C. использовать стороннюю библиотеку, написанную на C ++).   -  person Marc W    schedule 11.05.2009
comment
Практически точная копия stackoverflow.com/questions/649483/   -  person Barry Wark    schedule 11.05.2009
comment
@ Mark W - не надо этого делать, почему бы и нет? Я использовал ObjC ++, и он отлично работает. Я могу использовать #import ‹map› и @property std :: map ‹int, NSString *› myDict; Я могу использовать полный API Какао И иметь строго типизированные коллекции. Я не вижу недостатков.   -  person John Henckel    schedule 29.04.2014
comment
Apple только что представила дженерики вместе с Xcode 7.   -  person Pedro Mancheno    schedule 09.06.2015


Ответы (11)


В 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

person Connor    schedule 08.06.2015
comment
Поскольку у меня есть вопрос об общих типах и типах, возвращаемых в методах, я задал свой вопрос в другом потоке, чтобы все было понятно: stackoverflow.com/questions/30828076/ - person lvp; 14.06.2015
comment
@rizzes. Да, это только что представили. - person Connor; 29.07.2015
comment
Одно предостережение заключается в том, что Swift не полностью игнорирует аннотации типов в вашем универсальном классе ObjC. Если вы укажете ограничения, например MyClass <Foo: id<Bar>>, ваш код Swift будет предполагать, что значения являются типом вашего ограничения, что дает вам что-то для работы. Однако специализированные подклассы MyClass будут игнорировать их специализированные типы (фактически они будут выглядеть так же, как и общий MyClass). См. github.com/bgerstle/LightweightGenericsExample. - person Brian Gerstle; 01.10.2015
comment
Так компилируется ли это для операционных систем 10.10, 10.9 и более ранних? - person p0lAris; 22.10.2015
comment
Это должно быть, если вы установили цель развертывания для их поддержки - person Connor; 22.10.2015
comment
Swift 3 должен импортировать облегченные дженерики: github.com/apple/swift-evolution/blob/master/proposals/ - person Jeff Kelley; 18.05.2016

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


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

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

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

person Marc W    schedule 11.05.2009
comment
Я не согласен не беспокоиться, просто отправлять сообщения этим объектам. Если вы поместите в коллекцию объекты неправильного типа, которые не отвечают на эти сообщения, это приведет к ошибкам выполнения. Использование дженериков на других языках позволяет избежать этой проблемы с проверками времени компиляции. - person henning77; 04.01.2012
comment
@ henning77 Да, но Objective-C более динамичный язык, чем эти языки. Если вам нужна строгая безопасность типов, используйте эти языки. - person Raffi Khatchadourian; 16.01.2012
comment
Я также не согласен с философией не беспокойтесь - например, если вы вытащите первый элемент из NSArray и передадите его в NSNumber, но этот элемент на самом деле был NSString, вы облажались ... - person jjxtra; 20.01.2012
comment
Возможно, вы все слишком много читаете в этой формулировке, но я понимаю, как вы могли прийти к выводу, что я имел в виду, что никаких ошибок не может произойти (это не то, что я имел в виду). - person Marc W; 20.01.2012
comment
@Marc: тогда подумайте об изменении формулировки? - person bacar; 29.01.2012
comment
@PsychoDad Я согласен с вами, на самом деле всегда полезно проверять, какой объект вы используете, в Java проще, но вы можете объявить коллекцию типа Object, если вам нужно. - person AR89; 15.10.2012
comment
@RaffiKhatchadourian - не лучший выбор, если вы пишете приложение для iOS. Если бы было просто написать приложение на Java и получить все преимущества написания нативного приложения, поверьте мне: я бы стал. - person ericsoco; 28.01.2013
comment
Самая большая жалоба, которую я имею по этому поводу, связана не с динамическими языками и проверкой времени компиляции, а с простым общением с разработчиками. Я не могу просто смотреть на объявление свойства и знать, какой тип объектов он собирается вернуть, если это где-то не задокументировано. - person devios1; 14.03.2013
comment
@chaiguy это вопрос динамического и статического IMO - person funkybro; 02.04.2013
comment
@ericsoco, возможно, Xamarin может быть хорошим вариантом, если вы не против потратиться на него; C # IMHO имеет много преимуществ перед Java и несколько недостатков. По сравнению с Objective-C ... ну, он несколько выигрывает, когда дело доходит до таких функций. - person PeterT; 09.06.2013
comment
В этом случае подойдет проверка статического типа. В любом случае хороший способ немного смягчить эту проблему - использовать утверждения, чтобы убедиться, что объект имеет тот тип, который вы хотите, - иначе он потерпит неудачу. Я использую что-то похожее на assert_class(object, [NSString class]) в своем коде после получения объекта. Также важно документировать тип объектов, содержащихся в массиве. - person diegoreymendez; 14.08.2013
comment
Дженерики в Java тоже не нужны. Они здесь из-за страсти к C ++. - person Hot Licks; 28.08.2014

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

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

or

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
person Mark Rhodes    schedule 19.11.2010
comment
Думаю, это хороший способ документировать это на случай, если кто-то еще прочитает ваш код. В любом случае имя переменной должно быть максимально ясным, чтобы знать, какие объекты она содержит. - person htafoya; 16.12.2012

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

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

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

person Matthew Vines    schedule 11.05.2009
comment
Ссылка на документ в ответе мертва - К сожалению, эта страница не может быть найдена. - person Pang; 21.03.2018

Apple добавила дженерики в ObjC в XCode 7:

@property NSArray<NSDate *>* dates;
- (NSArray<NSDate *> *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray<NSString *> *)timestamps;

см. здесь: https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6-ID61

person user1259710    schedule 08.06.2015

Это было выпущено в 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.

person Kevin    schedule 09.06.2015

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

- (id)objectAtIndex:(NSUInteger)index

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

@interface NSStringArray : NSArray

as

- (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>, но вы можете выбрать свою собственную схему именования, чтобы пометить их как классы как шаблоны.

person w-m    schedule 14.03.2013
comment
обратите внимание, что то же самое существует в CoreLib: github.com/ core-code / CoreLib / blob / master / CoreLib / CoreLib.h # L105 - person user1259710; 10.06.2015

Взгляни на:

https://github.com/tomersh/Objective-C-Generics

Похоже, что это что-то вроде дженерика для бедняков, перепрофилировавшего механизм проверки протокола.

person David Jeske    schedule 01.08.2013

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

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

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

person htzfun    schedule 09.06.2015

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

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

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

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

person drekka    schedule 16.06.2015

Классы коллекций, предоставляемые фреймворками 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];
person Chris Reid    schedule 28.09.2012