Всеки срещу AnyObject в Swift 3.0

Когато срещнах тези псевдоними от два типа, докато анализирах JSON данните за първи път, нямах идея как да ги различавам или прилагам правилно. И така, какви са те? Any и AnyObject са два специални типа в Swift, които се използват за работа с неспецифични типове.

Според документацията на Apple Swift,

  • Всеки може изобщо да представлява екземпляр от всякакъв тип, включително типове функции и незадължителни типове.
  • AnyObject може да представлява екземпляр от всеки тип клас.

Добре, достатъчно просто - всеки се използва за всички видове, AnyObject се използва за типове клас, нали?

За да разбера как те наистина се държат в кода, реших да играя с тях в детска площадка.

Всеки пример

Всеки ми позволи да работя с комбинация от различни типове, включително функционални и некласови типове като Int, String и Bool. Според документацията елементите в този масив са структури, които са типови типове, така че на теория AnyObject не трябва да работи в тези случаи.

За да проверя това, се опитах да включа Strings и Ints, които са стойностни типове в Swift, използвайки AnyObject.

Грешка AnyObjectArray при включване на типове структури

Както се очакваше, компилаторът ми хвърли грешка, казвайки, че елементите не съответстват на типа AnyObject в масива. Хванах те!

Тогава това странно нещо се случи, когато се опитах да следвам предложенията на съставителя:

Елементи, отправени към AnyObject

Какво се случи току що?! Как успях да използвам AnyObject за Ints и Strings, като изрично излъчих всеки елемент към AnyObject?

След това отпечатах anyObjectArray в конзолата.

Отпечатване на anyObjectArray

Елементът Hi явно ми изглеждаше като низ, но нямаше цитати като нормална стойност на String в Swift!

След това отпечатах всеки елемент, използвайки контур за вход, за да проверя действителния му тип, а не неговия кастиран тип AnyObject.

Първо използвах оператора, за да видя дали елементите са типове Swift Struct или не.

Проверка на типове елементи в anyObjectArray 1

Той е от тип String! Тогава как би могло да се предава на AnyObject? Отново струните в Swift са структури, а не класови типове. По този начин на теория не бих могъл да ги представям като AnyObject.

Какво?

Бях напълно объркан и реших да направя още няколко експеримента с него. Този път използвах NSNumber и NSString, които са типове Objective-C, за да проверя типа на всеки елемент.

Проверка на типове елементи в anyObjectArray 2

Чакай, Здравейте също е NSString и числовите елементи са NSNumber! И ... те са референтни типове в Objective-C! Това ли беше причината, поради която Здрасти нямаше цитати в конзолата? Написах още код, по-долу, за да видя дали предположението ми е правилно.

Печат на NSString Array и String ArrayЗдравейте без включени котировки в конзолата като NSString

Потвърдено! Елементите, които са кастирани към AnyObject в масива, сега са класови типове Objective-C: NSString и NSNumber.

И така ... Какво всъщност става под капака? Продължих да копая в тази тема и намерих най-правдоподобния отговор от документа Използване на Swift с какао и Objective-C (Swift 3.0.1).

Като част от своята оперативна съвместимост с Objective-C, Swift предлага удобни и ефективни начини за работа с какаови рамки. Swift автоматично преобразува някои типове Objective-C в типове Swift, а някои видове Swift в типове Objective-C. Видовете, които могат да бъдат преобразувани между Objective-C и Swift, се наричат ​​мостови типове.
Навсякъде, където можете да използвате мостово референтен тип Objective-C, вместо това можете да използвате типа стойност на Swift. Това ви позволява да се възползвате от функционалността, налична при реализацията на референтния тип, по начин, естествен за Swift код. Поради тази причина почти никога не трябва да използвате мостово референтен тип директно в собствения си код. В действителност, когато кодът Swift импортира API-та на Objective-C, вносителят замества референтните типове Objective-C със съответните им стойности. По същия начин, когато кодът Objective-C импортира API на Swift, вносителят също заменя типовете стойности на Swift със съответните им референтни типове Objective-C. “

С други думи, компилаторът прави всичко възможно да бъде гъвкав в работата с такива типове чрез автоматично преобразуване и мостове, като същевременно предотвратява лесното ни сриване на приложението. Брилянтно!

И така, кога всъщност използваме AnyObject? Както е посочено в документацията на Apple, AnyObject може да се използва за работа с обекти, произлизащи от Class, но не споделят общ root клас.

Но абсолютно необходимо ли е да го използваме в нашия код?

Моят отговор на този въпрос е: Не.

Apple казва:

В Swift 3 типът id в Objective-C сега се преобразува в Any type в Swift, който описва стойността на всеки тип, независимо дали е клас, enum, структура или друг тип Swift. Тази промяна прави API-те на Objective-C по-гъвкави в Swift, тъй като дефинираните типове стойности на Swift могат да бъдат предавани на API-та на Objective-C и извлечени като Swift типове, като елиминира необходимостта от ръчни „полета“.
Тези предимства се разпростират и върху колекции: Типове колекции Objective-C NSArray, NSDictionary и NSSet, които по-рано са приемали само елементи на AnyObject, сега могат да съдържат елементи от всякакъв тип. За хешираните контейнери, като речник и Set, има нов тип AnyHashable, който може да побере стойност от всеки тип, съответстваща на протокола Swift Hashable.

Изглежда, че сам по себе си работи отлично в свързването на тези два езика в Swift 3, без да е необходимо използването на AnyObject!

И така, какво беше крайното разсъждение зад тези промени?

С техните думи Apple обяснява:

Swift 3 интерфейси с API на Objective-C по-мощен начин от предишните версии. Например, Swift 2 картографира типа id в Objective-C към типа AnyObject в Swift, който обикновено може да съдържа само стойности от типове класове. Swift 2 също предостави неявни преобразувания в AnyObject за някои мостови типове стойности, като String, Array, Dictionary, Set и някои числа, като удобство, така че родните типове Swift да могат да се използват лесно с API на какао, които очакват NSString, NSArray, или другите класове контейнери от Foundation. Тези преобразувания не съответстват на останалата част от езика, което затруднява разбирането какво точно може да се използва като AnyObject, което води до грешки.

Човек би настоял обаче, че ние, разработчиците на iOS, винаги трябва да сме максимално специфични по отношение на използването на типове в код.

Всъщност Apple препоръчва:

Използвайте Any и AnyObject само когато изрично се нуждаете от поведението и възможностите, които те предоставят. Винаги е по-добре да бъдете конкретни за типовете, с които очаквате да работите във вашия код.

Помислете за този сценарий: Работим с номер 12.5 в Swift. В този случай бихме посочили конкретно, че е тип Double или Float, а не да го декларираме от тип Any. По този начин можем да имаме достъп до различни свойства или методи, които са налични за този конкретен тип. В този контекст бихме използвали AnyObject за класове, защото те са малко по-специфични от Any. Но отново, използването на AnyObject е само опция.

Надявам се тази публикация в блога да е помогнала на много от вас страхотни разработчици там за изясняване на Any и AnyObject. Използвайте всяко уверено в Swift 3, докато работите с API, поддържани от Objective-C.

Благодаря ви за четенето и щастливото кодиране!