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


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

Dictionary<int, MyCustomObject>

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

11 137

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

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

этот ответ устарел, но остается для исторической ценности. Начиная с 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

как

- (NSString *)objectAtIndex:(NSUInteger)index

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

созданный подкласс может быть использован в качестве замены drop-in и приносит много полезных функций: предупреждения компилятора, доступ к свойствам, лучшее создание кода и-завершение в 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). Это не шутка - на официальная страница из Swift:

новые синтаксические функции позволяют писать более выразительный код, улучшая согласованность по всему языку. SDK использовали новые функции Objective-C, такие как дженерики и аннотации nullability, чтобы сделать 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];