Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!
Zadejte Vaši e-mailovou adresu:
Kamarád fotí rád?
Přihlas ho k odběru fotomagazínu!
Zadejte e-mailovou adresu kamaráda:
Software
Více o XIBech
24. srpna 2011, 00.00 | V předchozím dílu našeho seriálu o programování počítačů s operačními systémy Mac OS X a iOS ve vývojovém prostředí Xcode jsme se začali zabývat "XIBy", sloužícími převážně (ale ne výhradně) k tvorbě grafického uživatelského rozhraní. Dnes si toho o nich řekneme více.
Minule jsme si podrobně vysvětlili, co je to NIB a XIB a jaký je mezi nimi vztah. Víme, že jde prakticky o totéž, NIB je skupina archivovaných objektů (typicky, ale ne nutně, objektů tvořících GUI), jež se jako celek načte do aplikace; XIB je pouze textová podoba NIBu, používaná při programování pro výhody textových formátů při správě a údržbě projektů. V následujícím textu proto budeme užívat obou termínů záměnně – "obsah NIBu" nebo "obsah XIBu" je totéž, rozdíl mezi nimi budeme činit jen ve výjimečných případech, kdy bude hrát roli to, zda hovoříme o projektu před sestavením aplikace nebo o sestavené aplikaci.
Když jsme si ukázali princip na praobyčejném objektovém archivu, řekli jsme si také, že skutečný NIB nabízí pár specializovaných služeb pro větší pohodlí. Dnes se na ně podíváme trochu podrobněji.
Vazby mezi objekty
Žádný objekt není ostrov sám pro sebe; užitečným se stává teprve díky vazbám na ostatní objekty, s nimiž komunikuje (a jež komunikují s ním) prostřednictvím zpráv. Nejjednodušší a nejběžnější vazba je přímý odkaz na jiný objekt – prostě instanční proměnná nebo atribut typu id (nebo, samozřejmě, v praxi častěji VhodnáTřída*), obsahující adresu spolupracujícího objektu.
Vazby uvnitř XIBu
Hned zpočátku je vhodné si uvědomit, že objektové vazby mezi objekty uvnitř XIBu nejsou žádný problém a korektně fungují i bez jakýchkoli doplňků nebo rozšíření.
Stejně jako objekt při archivaci ukládá do archivu své atributy typu rozměrů rámce nebo textu, zobrazovaného v instanci třídy UILabel, ukládá také odkazy na ostatní objekty, jež udržuje ve svých instančních proměnných nebo atributech. V dílu našeho seriálu věnovaném archivaci objektových sítí jsme si ukázali zprávu encodeConditionalObject:forKey:, jež slouží právě pro tento účel – a můžeme se samozřejmě spolehnout, že archivované objekty ji budou korektně používat.
Příkladem takové vazby, jež zůstává bez problémů v archivu zachována, je třeba právě struktura rámců, již jsme si ukázali v našem příkladu minule – to, že okno obsahuje dvě instance třídy UILabel a jeden UIButton není ničím jiným.
Vnější vazby
Horší je to s vazbami, jež spojují objekty v XIBu s objekty mimo něj. Služba encodeConditionalObject:forKey: takový externí odkaz samozřejmě do objektového archivu neuloží; po načtení se pak na jeho místě objeví hodnota nil. O vazbách vedených z vnějších objektů do XIBu samozřejmě ani nemá smysl hovořit: tyto objekty – existují-li vůbec – se o rozbalení objektového archivu vůbec "nedozvědí", a jejich vazby tedy zůstanou prázdné.
Ačkoli by bylo v principu možné tyto vazby vždy po načtení NIBu do paměti obnovit programově, bylo by to nepohodlné. Zde proto nastupuje prvá ze specializovaných služeb pro větší pohodlí: zástupné objekty.
Vlastně je to celkem jednoduché: nyní už samozřejmě nepředpokládáme, že bychom objektovou síť sestavovali programově a pak ukládali do souboru; namísto toho ji budeme vždy vytvářet ve specializovaném editoru. Díky tomu není problém do této sítě přidat jeden speciální objekt navíc – půjde o instanci nějaké naší speciální třídy, takové, aby instance mohla obsahovat libovolné vazby na libovolné objekty. Technicky to není vůbec žádný problém; prostě místo toho, abychom tyto vazby ukládali do instančních proměnných, použijeme pro ně třeba atribut bindings třídy NSMutableDictionary, kde vazbu "jež patří do instanční proměnné foo" uložíme pod klíč "foo". Při ukládání archivu pak právě tuto naši speciální instanci použijeme jako "root object" – vazby na vše ostatní, co v archivu potřebujeme (např. tedy v našem příkladu na okno, jež chceme zobrazit), budou navázány právě na ni.
Pak využijeme toho, že API nabízí služby pro přístup k atributům nebo instančním proměnným objektu podle jména – podrobně jsme si toto API, nazývané Key/Value Coding nebo zkráceně KVC, popsali již před časem – a můžeme načítání objektového archivu naprogramovat zhruba takto (jde jen o koncepci, v praxi by kód byl samozřejmě trochu složitější kvůli technickým drobnostem):
-(BOOL)loadArchiveFromPath:(NSString*)path owner:owner {
id root=[NSKeyedUnarchiver unarchiveObjectWithFile:path];
if (!root) return NO;
NSDictionary *bd=[root bindings];
for (NSString *key in bd)
[owner setValue:[bd objectForKey:key] forKey:key];
return YES;
}
Nyní je zřejmé, že všechny objektové vazby, jež vedly z našeho zástupného objektu do ostatních objektů v archivu jsou korektně přeneseny do "lokálního" objektu owner (pokud má vhodný atribut nebo instanční proměnnou; nemá-li je, dojde k chybě).
Ještě je třeba vyřešit podobným způsobem naopak vazby z ostatních objektů v archivu na root (kód by byl velmi podobný, opět je třeba, aby byl v "rootu" seznam těchto vazeb, a pak se pomocí metody setValue:forKey: nahradí ve všech nově načtených objektech odkaz na objekt "root" odkazem na objekt "owner") a jsme hotovi: nejenže je objektová síť korektně načtena, ale také jsou korektně navázána všechna propojení mezi ní a objektem, který jsme vybrali jako jejího vlastníka.
Za zmínku možná stojí pár drobností:
• na přesně témže principu můžeme do archivu uložit více různých zástupných objektů;
• povšimněte si, že jsme ve výše uvedeném pseudokódu načtené objektové síti neposílali zprávu retain: je samozřejmě věcí objektových vazeb v "owneru", aby si cílové objekty přidržely samy;
• v důsledku minulého bodu automaticky zanikne i "root";
• skutečná třída "ownera" vůbec nehraje roli: podstatné je, aby dokázal interpretovat korektně nastavení všech atributů jako příjemce zprávy setValue:forKey:; jak přesně to ale udělá je lhostejné. Stejně dobře to může být instance třídy obsahující odpovídající instanční proměnné, nebo NSMutableDictionary – nebo třeba nějaká třída, v níž jsou implementovány odpovídající settery jako třídní metody!
Mimochodem, výše popsaný mechanismus s "neretainovanými" daty, jež si musí "podržet" vlastník, se skutečně používá v iOSu. V prostředí Cocoa systému Mac OS X je tomu z historických důvodů naopak – tam jsou kořenové objekty z NIBu automaticky "retainovány", a vlastník se musí postarat o jejich uvolnění, pokud to je zapotřebí (typicky třeba pokud NIB representoval obsah okna a toto okno se zavírá).
A co "akce"?
Vazby mezi objekty ovšem nejsou pouze jednoduché odkazy; naprosto běžně se setkáváme také s "akcemi" – tedy s kombinací zprávy, již je zapotřebí odeslat, a cílového objektu, jemuž se tato zpráva má doručit: typicky např. stanovíme, že po stisknutí toho a toho tlačítka dostane objekt foo zprávu bar.
Komplikuje nám to nějak výše popsaný mechanismus?
Vůbec ne.
Akce ve skutečnosti není nic jiného, než kombinace dvou proměnných: odkazu na objekt – přesně a do posledního detailu stejnému odkazu, jako ty, o nichž jsme se bavili před chvilkou – a proměnné typu SEL, obsahující selektor zprávy, již je zapotřebí objektu poslat (o selektorech jsme se bavili hned na samém začátku, když jsme si vysvětlovali, jak funguje jazyk Objective C).
Jediná speciální podpora, již tedy potřebujeme pro "akce", je na úrovni editoru XIBů: ten musí být schopen pohodlným způsobem editovat celou dvojici proměnných najednou jako "akci poslanou objektu". Z hlediska vnitřní struktury XIBu však nám zcela stačí to, co máme už dávno k dispozici.
(V iOSu je to jen malinko složitější po technické stránce, neboť zde objekty mnohdy dovolují současné odeslání více různých akcí více různým cílům. Na principu věci to ale nemění zhola nic; jen musíme počítat s tím, že místo jednoduchého odkazu na jeden cílový objekt a jednoduché proměnné typu SEL máme v obou případech pole o libovolném počtu prvků.)
Specifické podtřídy
Poslední se specialisovaných služeb pro větší pohodlí, jež si podrobněji vysvětlíme, je podpora pro specifické podtřídy.
Dejme tomu, že v našem příkladu měl na místě prvé instance UILabel ve skutečnosti stát objekt třídy MySmartLabel, který rozšiřuje standardní UILabel o nějaké formátování, specifické pro naši konkrétní aplikaci.
V původní variantě, kdy jsme kompletní GUI sestavili programově přímo v cílové aplikaci a tam také uložili do archivu (a z něj opět načtli), bylo samozřejmě vše v pořádku.
Problém ale nastane ve chvíli, kdy tento archiv budeme chtít otevřít v obecném editoru objektových sítí: třída MySmartLabel v tomto editoru samozřejmě není k dispozici, a načítání archivu skončí chybou.
Co s tím?
Už před lety našli ve firmě NeXT elegantní a účinné řešení: při archivaci NIBu objekty těchto speciálních tříd neukládáme přímo; namísto toho
• třídu nahradíme její podtřídou, jež ve standardních knihovnách k dispozici je (v našem případě tedy UILabelem, jindy by to mohlo být třeba UIView nebo dokonce NSObject);
• doplníme ji ale záznamem, obsahujícím jméno skutečně požadované třídy;
• a k tomu ještě přidáme seznam "outletů" a "akcí", jež skutečně požadovaná třída má navíc oproti té, kterou jsme ji nahradili.
Editor XIBů pak jen vezme tento seznam v úvahu a objekt – který nyní hraje tak trochu roli zástupného – opatří podobným slovníkem pro navazování "neznámých" odkazů, jaký se používá u zástupných objektů: to proto, aby bylo možné pracovat s odkazy oné neznámé podtřídy.
To je vlastně celé: při načítání NIBu do aplikace se samozřejmě použije skutečné jméno požadované třídy, a objektové vazby se opět naváží pomocí slovníku, podobně, jako tomu bylo u objektů zástupných.
Obsah seriálu (více o seriálu):
- Nastal čas na kakao...
- Tak nejdřív kakao ochutnáme...
- Programovací jazyk C: velmi, velmi stručně
- Objective C: to si vysvětlíme podrobněji
- Co jsme si o Objective C ještě neřekli...
- Nastal čas na kakao - Vznik a zánik objektů
- Nastal čas na kakao - Kopírování objektů
- Nastal čas na kakao - Skryté podtřídy
- Nastal čas na kakao - Základní služby objektů
- Nastal čas na kakao - Jak správně psát v Objective C
- Nastal čas na kakao - Jak správně importovat
- Nastal čas na kakao - Podtřídy, delegáti, vkládání, jak se to rýmuje?
- Nastal čas na kakao - Využití kategorií namísto dědičnosti
- Nastal čas na kakao - Vkládání objektů a přesměrování zpráv
- Nastal čas na kakao - Inicializace a rušení objektů
- Nastal čas na kakao - Metody initWith... a designovaný inicializátor
- Nastal čas na kakao - Inicializace: tipy a triky
- Nastal čas na kakao - Accesory: přístup k proměnným instancí
- Nastal čas na kakao - Šedá je teorie, zelený je strom života...
- Nastal čas na kakao - Více o XCode: inspektory
- Nastal čas na kakao - Aplikace RSS2: datový model
- Nastal čas na kakao - Aplikace RSS: implementace datového modelu
- Nastal čas na kakao - Aplikace RSS: parsování XML
- Nastal čas na kakao - Interface Builder a uživatelské rozhraní
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Druhý kontrolér a dokončení aplikace
- Nastal čas na kakao - Drobná vylepšení a zdokonalení...
- Nastal čas na kakao - Ladění
- Nastal čas na kakao - Třídy Foundation Kitu
- Nastal čas na kakao - Třídy Foundation Kitu (2)
- Nastal čas na kakao - Textové řetězce: NS(Mutable)String
- Nastal čas na kakao - Čísla, binární data a další...
- Nastal čas na kakao - Archivace objektů
- Nastal čas na kakao - Trocha magie, aneb distribuované objekty
- Nastal čas na kakao - Málem bychom zapomněli: NSAutoreleasePool
- Nastal čas na kakao - Zpracování výjimek: NSException
- Nastal čas na kakao - NSInvocation a černá magie
- Nastal čas na kakao - Kakao v Tygrovi
- Nastal čas na kakao - Notifikace: nepřímé předávání zpráv
- Nastal čas na kakao - NSUserDefaults
- Nastal čas na kakao - Co nového ve Foundation Kitu
- Nastal čas na kakao – s Intelem, s Intelem, jedeme do...
- Co nového v Xcode
- Začínáme s AppKitem
- Jak MVC v Kakau vypadá doopravdy?
- Jak MVC v Kakau vypadá doopravdy: dokončení
- Přehled tříd AppKitu
- Nastal čas na kakao - Přehled tříd AppKitu 2
- Přehled tříd AppKitu 3: zbývající třídy GUI
- Přehled tříd AppKitu 4: textový systém
- Nastal čas na kakao - Přehled tříd AppKitu 5: hlavně grafika
- Přehled tříd AppKitu 6: dokumentový systém
- Přehled tříd AppKitu 7: dokončení
- Pojmenované vlastnosti objektů
- Pojmenované vlastnosti objektů: implementace
- Pojmenované vlastnosti objektů: relace 1:N
- Pojmenované vlastnosti objektů: řazení jmen a agregační funkce
- Sledování změn objektů
- Sledování změn objektů – ukázka
- Sledování změn objektů – zdrojový kód
- Sledování změn objektů: kód modelu
- Sledování změn objektů: přímý přístup
- Kontroléry a vazby
- Vázání vazeb
- Další vazby s jednoduchým kontrolérem
- Implementace a použití převodu hodnot
- Validace hodnot
- Validace a chyby, a jedna hezká vazba...
- Práce s polem objektů
- Základní vazby NSArrayControlleru
- Převodníky, přepínače, placeholdery
- Mírná vylepšení v mezích zákona
- Objective C 2.0 - novinky z Leoparda
- NSTreeController
- Programování v Cocoa - Pár tipů a triků
- Programování v Cocoa - Základy kreslení
- Kterak nakreslit modrý obdélník...
- Další služby pro kreslení
- Obrázky a písmenka...
- Události a myš
- Lepší práce s myší
- Události klávesnice
- Input Management
- Příkazy a schránka
- Další události
- Táhni a padni
- Byli jsme na tahu; nyní padneme.
- Zvolme si, jak vhodit
- Drobnosti a chybičky
- Speciální případy tahání či házení
- Kterak táhnout něco, co neexistuje?
- Jak na sítě...
- NSURLConnection
- Safari za minutu
- Služby WebKitu
- Kakao v Leopardu
- Druhé Objective C
- Druhé Objective C: různé drobnosti
- Druhé Objective C: kategorie a protokoly
- Druhé Objective C: nový příkaz cyklu
- Druhé Objective C: atributy a accesory
- Druhé Objective C: atributy a accesory
- 64 je dvakrát 32
- Ubicumque dulce est, ibi et acidum invenies...
- Irbis: že prý žádné novinky?
- Blok sem, blok tam, nám už je to všechno jasné...
- Bloky jsou i v AppKitu
- Irbis a Foundation Kit
- Kde jsou má data?
- Kde jsou má data? V NSCache!
- Soubor, jméno, URL, jak se to rýmuje...
- Další podpora NSURL
- Zabíjení!
- A máme tady i...OS!
- Systémové prvky GUI
- Programování pro iOS 1. díl - Rozdíly mezi "i" a "Mac"
- Programování pro iOS - 2. Začínáme programovat
- Programování pro iOS - 3. základní ovladače a propojení GUI s kódem
- Programování pro iOS - 4. Varovná hlášení
- Programování pro iOS - 5. Rámce a jejich řídicí objekty
- Programování pro iOS - 6. Ukládání dat
- Programování pro iOS - 7. Správa paměti a starý restík
- Programování pro iOS - 8. Dokončení aplikace
- Programování pro iOS - 9. Jak dostat aplikaci do iPhone
- Programování pro iOS - 10. Instalace aplikace do cizího iPhone
- Programování pro iOS - 11. Jak dostat aplikaci do libovolného iPhone
- Programování pro iOS - 12. Touching!
- Programování pro iOS - 13. Kreslíme na iPhone
- Programování pro iOS - 14. Udělejme gesto
- Programování pro iOS - 15. Další gesta
- Programování pro iOS - 16. Více prstů, více zábavy
- Programování pro iOS - 17. Podpora standardních gest
- Programování pro iOS - 18. Recognizery v iOS
- Programování pro iOS - 19. Další standardní recognizery
- Programování pro iOS - 20. Co nového v iOSu
- Programování pro iOS - 21. "Multitasking"
- Programování pro iOS - 22. Nulla est honesta avaritia nisi temporis
- Programování pro iOS - 23. Jak se aktivovat, jsme-li v pozadí
- Programování pro iOS - 24. Zbývající drobnosti
- Programování pro iOS - 25. Řídicí objekty rámců
- Programování pro iOS - 26. Jak se dělá UIViewController
- Programování pro iOS - 27. Kde vzít rámce
- Programování pro iOS - 28. Základní služby
- Programování pro iOS - 29. Práce s rámci
- Programování pro iOS - 30. Rotace zařízení
- Programování pro iOS - 31. Správa paměti v rámcích
- Programování pro iOS - 32. Řídicí objekt pro tabulky
- Programování pro iOS - 33. Řídicí objekt pro strom
- Programování pro iOS - 33. Více o UINavigationControlleru
- Programování pro iOS - 35. Ještě jednou UINavigationController
- Programování pro iOS - 36. Po navigátoru taby
- Programování pro iOS - 37. Více o UITabBarControlleru
- Programování pro iOS - 38. Dokončení UITabBarControlleru
- Programování pro iOS - 39. UIPopoverController
- Programování pro iOS - 40. Další triky UIPopoverControlleru
- Programování pro iOS - 41. Zbývající služby UIPopoverControlleru
- Programování pro iOS - 42. UISplitViewController
- Programujeme v
iTunesXcode 4 - Programování pro iOS - 44. Předvolby Xcode 4
- Programování pro iOS - 45. Práce v Xcode 4
- Xcode 4: projekt a cíle
- Xcode 4: práce s cíli
- Xcode 4: Build Settings
- Xcode 4: Build Phases
- Xcode4: Build Phases podruhé
- Xcode 4: Co jsou to Build Rules?
- Xcode4: taje editoru
- Xcode4: automatické doplňování v editoru
- XIBy chyby
- Více o XIBech
- Editor XIBů
- Inspektory pro XIBy
- Vazby mezi objekty v XIBech
- Vazby mezi objekty v kódu
- Paletky Xcode pro XIBy
- Xcode 4: levý sloupec
- Xcode 4: okno Organizer
- Xcode 4: okno Organizer, část druhá
- Xcode 4: co je to Workspace?
- Xcode 4: základy schémat
- Xcode 4: akční schémata