Libovolný vs. AnyObject ve verzi Swift 3.0

Když jsem se poprvé setkal s těmito dvěma aliasy při analýze dat JSON, neměl jsem tušení, jak je správně rozlišit nebo implementovat. Co jsou tedy? Any a AnyObject jsou dva speciální typy v Swift, které se používají pro práci s nespecifickými typy.

Podle dokumentace společnosti Swift společnosti Apple

  • Kdekoli může představovat instanci libovolného typu, včetně typů funkcí a volitelných typů.
  • AnyObject může představovat instanci libovolného typu třídy.

Dobře, dostatečně jednoduché - Každý se používá pro všechny typy, AnyObject se používá pro typy třídy, že?

Abych pochopil, jak se v kódu chovají, rozhodl jsem se s nimi hrát na hřišti.

Jakýkoli příklad

Kdokoli mi dovolil pracovat se směsí různých typů, včetně funkčních a neklasifikovaných typů, jako jsou Int, String a Bool. Podle dokumentace jsou prvky v tomto poli strukturami, které jsou typy hodnot, takže v těchto případech by teoreticky AnyObject neměl fungovat.

Abych to ověřil, pokusil jsem se zahrnout Strings a Ints, což jsou typy hodnot v Swift, pomocí AnyObject.

Chyba AnyObjectArray při zahrnutí typů struktur

Podle očekávání mi kompilátor vyvolal chybu, když řekl, že prvky neodpovídají typu AnyObject v poli. Mám tě!

Pak se tato podivná věc stala, když jsem se snažil následovat návrhy kompilátoru:

Prvky obsažené v AnyObject

Co se právě stalo?! Jak jsem mohl použít AnyObject na Ints a Strings explicitním přenesením každého prvku do AnyObject?

Poté jsem vytiskl anyObjectArray do konzole.

Tisk anyObjectArray

Prvek Ahoj mi očividně vypadal jako řetězec, ale v Swift neměl kolem sebe citace jako normální hodnotu řetězce!

Dále jsem vytáhl každý prvek pomocí smyčky pro kontrolu, abych zkontroloval jeho skutečný typ, spíše než jeho přetypovaný typ AnyObject.

Nejprve jsem použil operátor, abych zjistil, zda jsou prvky typu Swift Struct nebo ne.

Kontrola typů prvků v anyObjectArray 1

Je typu String! Jak by to mohlo být přeneseno na AnyObject? Řetězce Swift jsou opět strukturami, nikoli typy tříd. Teoreticky bych je tedy neměl mít možnost obsadit jako AnyObject.

Co?

Byl jsem naprosto zmatený a rozhodl jsem se s ním udělat další experimenty. Tentokrát jsem použil NSNumber a NSString, což jsou typy Objective-C, pro kontrolu typu každého prvku.

Kontrola typů prvků v anyObjectArray 2

Počkejte, ahoj je také NSString a číselné prvky jsou NSNumber! A… jsou to referenční typy v Objective-C! To byl důvod, proč Ahoj neměl na konzole citace? Napsal jsem další kód, jak je uvedeno níže, abych zjistil, zda můj předpoklad byl správný.

Tisk NSString Array a String ArrayAhoj, bez nabídek na konzoli jako NSString

Potvrzeno! Prvky, které jsou sesílány do AnyObject v poli, jsou nyní typy třídy Objective-C: NSString a NSNumber.

Takže ... Co se to vlastně děje pod kapotou? Pokračoval jsem v kopání tohoto tématu a našel jsem nejpravděpodobnější odpověď z dokumentu Use Swift with Cocoa and Objective-C (Swift 3.0.1).

Jako součást své interoperability s Objective-C nabízí Swift pohodlné a efektivní způsoby práce s kostry Cocoa. Swift automaticky převede některé typy Objective-C na typy Swift a některé typy Swift na typy Objective-C. Typy, které lze převést mezi Objective-C a Swift, se označují jako přemostěné typy.
Kdekoli můžete použít přemostěný referenční typ Objective-C, můžete místo toho použít typ hodnoty Swift. To vám umožní využít funkcí dostupných při implementaci referenčního typu způsobem, který je přirozený v kódu Swift. Z tohoto důvodu byste téměř nikdy nemuseli používat přemostěný typ odkazu přímo ve svém vlastním kódu. Ve skutečnosti, když kód Swift importuje API rozhraní Objective-C, dovozce nahradí referenční typy Objective-C jejich odpovídajícími typy hodnot. Podobně, když kód Objective-C importuje rozhraní API Swift, dovozce také nahradí typy hodnot Swift odpovídajícími typy referencí Objective-C. “

Jinými slovy, kompilátor dělá vše pro to, aby byl flexibilní při manipulaci s takovými typy prostřednictvím automatické konverze a přemostění a zároveň zabraňoval snadnému selhání aplikace. Brilantní!

Kdy tedy skutečně používáme AnyObject? Jak je uvedeno v dokumentaci společnosti Apple, AnyObject lze použít pro práci s objekty, které pocházejí z třídy, ale nesdílejí společnou kořenovou třídu.

Je však nezbytně nutné použít jej v našem kódu?

Moje odpověď na tuto otázku zní: Ne.

Apple říká:

V Swift 3 se typ id v Objective-C nyní mapuje na Any type in Swift, který popisuje hodnotu libovolného typu, ať už jde o třídu, výčet, strukturu nebo jakýkoli jiný typ Swift. Tato změna zvyšuje flexibilitu rozhraní API Objective-C ve službě Swift, protože typy hodnot definované ve Swift lze předat do rozhraní API Objective-C a extrahovat jako typy Swift, což vylučuje potřebu ručních typů „boxů“.
Tyto výhody se vztahují také na kolekce: Typy kolekcí Objective-C NSArray, NSDictionary a NSSet, které dříve přijímaly pouze prvky AnyObject, nyní mohou obsahovat prvky libovolného typu. Pro kontejnery typu hash, jako jsou Slovník a Sada, existuje nový typ AnyHashable, který dokáže uchovat hodnotu libovolného typu v souladu s protokolem Swift Hashable.

Zdá se, že každý sám pracuje naprosto dobře při překlenutí těchto dvou jazyků ve Swift 3, aniž by bylo nutné používat AnyObject!

Jaký byl tedy konečný důvod těchto změn?

Apple svými slovy vysvětluje:

Swift 3 rozhraní s Objective-C API mnohem výkonnějším způsobem než předchozí verze. Například Swift 2 namapoval typ id v Objective-C na typ AnyObject v Swift, který normálně pojme pouze hodnoty typů tříd. Swift 2 také poskytoval implicitní konverze na AnyObject pro některé typy přemostěných hodnot, jako jsou String, Array, Dictionary, Set a některá čísla, jako výhodu, takže nativní typy Swift lze snadno použít s kakaovými API, která očekávaly NSString, NSArray, nebo jiné třídy kontejnerů od nadace. Tyto převody byly nekonzistentní se zbytkem jazyka, takže bylo obtížné pochopit, co přesně lze použít jako AnyObject, což vede k chybám.

Dalo by se však trvat na tom, že my i vývojáři iOS musí být vždy co nejkonkrétnější, pokud jde o používání typů v kódu.

Apple ve skutečnosti doporučuje:

Any a AnyObject používejte, pouze pokud výslovně potřebujete chování a možnosti, které poskytují. Vždy je lepší být konkrétnější o typech, s nimiž ve svém kódu očekáváte práci.

Přemýšlejte o tomto scénáři: Pracujeme s číslem 12,5 ve společnosti Swift. V tomto případě bychom konkrétně uvedli, že se jedná spíše o typ Double nebo Float než o deklaraci typu Any. Tímto způsobem můžeme pohodlně přistupovat k různým vlastnostem nebo metodám, které jsou pro daný typ k dispozici. V této souvislosti bychom použili AnyObject pro typy třídy, protože jsou trochu konkrétnější než Any. Ale opět, použití AnyObject je jen možnost.

Doufám, že tento blogový příspěvek pomohl mnoha z vás úžasných vývojářů venku při vyjasňování Any a AnyObject. Při práci s API podporovanými Objective-C používejte Any s jistotou.

Děkujeme za čtení a šťastné kódování!