Существуют ли строго типизированные коллекции в Objective-C?

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

Dictionary<int, MyCustomObject>

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

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, легкие дженерики Objective-C игнорируются Swift. Любые другие типы, использующие легкие дженерики, импортируются в Swift, как если бы они были непараметрированы.

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


этот ответ устарел, но остается для исторической ценности. Начиная с Xcode 7, ответ Коннора от июня 8 ' 15 более точен.


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

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

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


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

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

или

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...

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

документы

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


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


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


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


взгляните на:

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

похоже, это своего рода дженерики бедняка, путем перепрофилирования механизма проверки протокола.


теперь Мечты сбываются-есть дженерики в Objective-C с сегодняшнего дня (Спасибо, WWDC). Это не шутка!--1-->официальная страница из Swift:

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

и изображение, которое доказывает это:Objective-C generics


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

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

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

наслаждайтесь.


классы коллекций, предоставляемые фреймворками Apple и GNUStep, являются полу-универсальными, поскольку они предполагают, что им заданы объекты, некоторые из которых являются сортируемыми, а некоторые отвечают на определенные сообщения. Для примитивов, таких как floats, ints и т. д., Вся структура массивов C неповреждена и может использоваться, и для них есть специальные объекты-оболочки для использования в общих классах коллекции (например, NSNumber). Кроме того, класс коллекции может быть подклассом (или специально изменен через категории) чтобы принять объекты любого типа, но вы должны написать весь код обработки типов самостоятельно. Сообщения могут быть отправлены любому объекту, но должны возвращать значение 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];