Чи є в Objective-C сильно типізовані колекції?


140

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

Dictionary<int, MyCustomObject>

може містити лише ключі, які є цілими числами та значеннями типу MyCustomObject. Чи існує подібний механізм у Objective-C?


Починаю сам дізнаватися про ObjC. Можливо, ви можете використовувати ObjC ++ для важкого підйому?
Toybuilder

Можливо, вас зацікавлять відповіді на це запитання: чи є який-небудь спосіб застосувати введення тексту на NSArray, NSMutableArray тощо? . Наводяться аргументи, чому це не є звичайною практикою в «Об’єктив-С / Какао».
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 І мати сильно типізовані колекції. Я не бачу жодної сторони.
Джон Генкель

Відповіді:


211

У Xcode 7 Apple представила «Objective-C» для «Легких дженериків» на «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 3+ більше немає правдивості.)

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

Крім цих колекційних класів Foundation, легкі дженерики Objective-C ігноруються Swift. Будь-які інші типи, що використовують легкі дженерики, імпортуються в Swift так, ніби вони не мають параметри.

Взаємодія з API-кодами Objective-C


Оскільки у мене є питання про дженериків та типи повертаються в методах я запитав моє запитання в іншому потоці, щоб тримати все ясно: stackoverflow.com/questions/30828076 / ...
ППШ

2
@rizzes. Так, це було щойно представлено.
Коннор

Одним із застережень є те, що Swift не повністю ігнорує анотації типу у вашому загальному класі ObjC. Якщо ви вкажете обмеження, наприклад MyClass <Foo: id<Bar>>, ваш код Swift буде вважати, що значення є типом вашого обмеження, що дає вам з чим працювати. Однак спеціалізовані підкласи MyClassігнорували б їх спеціалізовані типи (їх слід розглядати так само, як і загальні MyClass). Дивіться github.com/bgerstle/LightweightGenericsExample
Брайан Герстл

Так це компілюється для операційних систем 10.10, 10.9 і більш ранніх?
p0lAris

Це повинно бути до тих пір, поки ви встановите ціль розгортання для їх підтримки
Коннор

91

Ця відповідь застаріла, але залишається історичною. Відповідно до Xcode 7, відповідь Коннора від 8 червня 15 року є більш точною.


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

Objective-C має динамічне введення тексту як особливість, а це означає, що час виконання не хвилює тип об'єкта, оскільки всі об’єкти можуть приймати повідомлення. Коли ви додаєте об'єкт до вбудованої колекції, вони просто обробляються так, ніби вони набрали тип id. Але не хвилюйтеся, просто надсилайте повідомлення тим об’єктам, як звичайні; він буде добре працювати (якщо, звичайно, один або кілька об’єктів у колекції не відповідають на повідомлення, яке ви надсилаєте) .

Дженріки потрібні в таких мовах, як Java та C #, оскільки вони є сильними, статично типовими мовами. Зовсім інша кульова гра, ніж динамічна функція набору тексту Objective-C.


88
Я не погоджуюся з "не хвилюйтеся, просто надсилайте повідомлення тим об'єктам". Якщо ви помістите в колекцію неправильний тип об’єктів, які не відповідають на ці повідомлення, це призведе до помилок виконання. Використання генерики іншими мовами дозволяє уникнути цієї проблеми при перевірці часу компіляції.
henning77

8
@ henning77 Так, але Objective-C є більш динамічною мовою, ніж ці мови. Якщо ви хочете забезпечити безпеку типу, використовуйте ці мови.
Раффі Хатчадуріан

36
Я також не погоджуюся з філософією не хвилюватися - наприклад, якщо ви витягнете перший предмет з NSArray і відкиньте його до NSNumber, але цей елемент був насправді NSString, ви
накрутили

13
@RaffiKhatchadourian - не великий вибір, якщо ви пишете додаток для iOS. Якби просто написати Java-програму та отримати всі переваги написання нативного додатка, повірте: я б.
ericsoco

11
Найбільша скарга на мене не стосується динамічних мов проти перевірки часу компіляції, а простого спілкування розробника. Я не можу просто переглянути декларацію про властивість і знати, який тип об’єктів він збирається повернути, якщо це десь не задокументовано.
devios1

11

Ні, але щоб було зрозуміліше, ви можете прокоментувати його типом об'єкта, який ви хочете зберігати, я вже кілька разів бачив це, коли вам потрібно щось написати на Java 1.4), наприклад:

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

або

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

Я думаю, це хороший спосіб задокументувати це, якщо хтось інший прочитає ваш код. У будь-якому випадку ім'я змінної повинно бути максимально зрозумілим, щоб знати, які об’єкти вона містить.
хтафоя

6

У Objective-C немає жодних дженериків.

Від Документів

Масиви - це упорядковані колекції об'єктів. Какао надає кілька класів масивів, NSArray, NSMutableArray (підклас NSArray) та NSPointerArray.


Посилання на документ у відповідь мертве - "Вибачте, цю сторінку неможливо знайти" .
Панг


5

Це було випущено в 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];

Будьте уважні до цих *с.


4

Загальні NSArrays можуть бути реалізовані шляхом підкласифікації NSArrayта перевизначення всіх наданих методів з більш обмежуючими. Наприклад,

- (id)objectAtIndex:(NSUInteger)index

доведеться переглядати

@interface NSStringArray : NSArray

як

- (NSString *)objectAtIndex:(NSUInteger)index

щоб NSArray містив лише NSStrings.

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

Це можливо автоматизувати і звести до лише двох тверджень, що наближає його до мов, що підтримують дженерики. Я створив автоматизацію з WMGenericCollection , де шаблони надаються як макроси C Preprocessor C.

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

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

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

Після створення NumberArrayви зможете використовувати його скрізь у своєму проекті. У ньому відсутній синтаксис <int>, але ви можете вибрати власну схему іменування, щоб позначити їх як класи як шаблони.


зауважте, що те саме існує в CoreLib: github.com/core-code/CoreLib/blob/master/CoreLib/CoreLib.h#L105
користувач1259710


2

Тепер мрії збуваються - з сьогодні існує Generics в Objective-C (спасибі, WWDC). Це не жарт - на офіційній сторінці Swift:

Нові функції синтаксису дозволяють писати більш виразний код, одночасно покращуючи узгодженість мови. У SDK були використані нові функції Objective-C, такі як загальна характеристика та анотація на зменшення якості, щоб зробити код Swift ще чистішим та безпечнішим. Ось лише вибірка вдосконалень Swift 2.0.

І зображення, що підтверджує це:Об’єктивна-С дженерики


2

Просто хочу заскочити сюди. Я написав повідомлення в блозі тут про дженериків.

Я хочу внести свій внесок у те, що Generics можна додавати до будь-якого класу , а не лише до класів колекції, як вказує Apple.

Потім я успішно додав до різноманітних занять, оскільки вони працюють точно так само, як і колекції Apple. тобто. компілювати перевірку часу, заповнення коду, що дозволяє знімати касти тощо.

Насолоджуйтесь.


-2

Класи колекцій, що надаються рамками Apple і GNUStep, є напівзагальними, оскільки вони припускають, що їм даються об'єкти, деякі сортуються, а деякі відповідають на певні повідомлення. Для примітивів, таких як поплавці, int тощо, вся структура масивів C є недоторканою і може використовуватися, і для них є спеціальні об'єкти обгортки для використання в загальних класах колекції (наприклад, NSNumber). Крім того, клас колекції може бути підкласифікований (або спеціально модифікований за допомогою категорій), щоб приймати об'єкти будь-якого типу, але вам потрібно написати весь код обробки даних самостійно. Повідомлення можуть бути надіслані будь-якому об’єкту, але вони повинні повернути нуль, якщо він не підходить для об'єкта, або повідомлення повинно бути перенаправлено відповідному об'єкту. Справжні помилки типу повинні бути зафіксовані під час компіляції, а не під час виконання. Під час виконання їх слід обробляти або ігнорувати. Нарешті, 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];
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.