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
Vazby mezi objekty v kódu
21. září 2011, 00.00 | V minulém dílu našeho programátorského seriálu jsme si ukázali, jak v Xcode sestavovat vazby mezi objekty; dnes se podíváme podrobněji na odpovídající implementaci v kódu.
"Drátování" mezi jednotlivými objekty v XIBech, jež umožní naplnění "outletů" a nastavení "akcí", jsme viděli podrobně minule. To samo ale stačí pouze v případech, kdy máme hotový objekt, nabízející nějaké události, schopné odesílat akce – třeba tlačítko – a také objekt, který potřebnou akci již kompletně implementuje.
Jakkoli takových objektů je na standardních knihovnách poměrně dost, daleko běžnější v iOSu je to, že většinu akcí i "outletů" implementujeme sami ve vlastních třídách (typicky jde o podtřídy UIViewControlleru, mnohdy také o vlastní rámce jako podtřídy UIView; jiné třídy v běžnějších projektech využíváme poměrně zřídkakdy).
Dnes se tedy podíváme podrobně na to, jak vypadá odpovídající kód.
Outlety
S "outlety" je situace celkem prostá. Vůbec nejjednodušší možný "outlet" je prostě instanční proměnná vhodného typu, označená v kódu navíc značkou IBOutlet – tu sice překladač Objective C ignoruje, ale Xcode podle ní rozezná, že má přístup k takové proměnné zajistit v XIBu:
@interface MyController:UIViewController {
IBOutlet UIButton *button;
}
@end
(Poznamenejme hned, že z hlediska zapouzdření obecně deklarace instančních proměnných v současném Objective C překládaném pomocí LLVM nepatří do hlavičkového souboru a za direktivu @interface, nýbrž do implementace a za direktivu @implementation; Xcode 4.0 zde však ještě nedokáže "outlety" najít. Dokud tedy nepřejdeme na novou versi vývojového prostředí – přičemž ale 4.1 je k dispozici pouze pro Lion, a 4.2 pouze v rámci bety iOS5 – musíme se zatím s deklaracemi "outletů" a "akcí" v hlavičkových souborech smířit.)
Je třeba mít na paměti, že při zavádění NIBu do paměti se tímto způsobem deklarovaná instanční proměnná vždy plní pomocí mechanismu KVC, který takto odkazovanému objektu vždy pošle zprávu retain. To znamená, že
• v iOSu, který ukládá obsah NIBů do autorelease poolu, se u objektů, jež takto navážeme na nějaký outlet, nemusíme obávat jejich ztráty;
• jak v iOSu, tak i v Mac OS X se ovšem musíme v metodě dealloc postarat o jejich uvolnění (pokud nevyužíváme služeb "třičtvrtěautomatického garbage collectoru" ARC, jímž se budeme zabývat až později);
• v iOSu také nesmíme zapomenout objekty načtené z NIBu, který je spravován třídou UIViewController (nebo některou z jejích podtříd) uvolnit v metodě viewDidUnload.
Chceme-li si tuto práci poněkud zjednodušit, můžeme využít alternativní deklarace "outletu" jako atributu; z hlediska objektového designu je to i čistší, ačkoli je to malinko víc psaní:
@interface MyController:UIViewController
@property (assign) IBOutlet UIButton *button;
@property (retain) IBOutlet UIView *alternativeView;
@end
(Samozřejmě zde nesmíme zapomenout v implementaci použít direktivu @synthesize, jež pro takto deklarovaný atribut vytvoří jak instanční proměnnou, jež nese jeho hodnotu, tak i odpovídající přístupové metody.)
Využijeme-li deklaraci s přepínačem assign, pak objekt, který navážeme na "outlet", zprávu retain nedostane – to je ideální v (daleko nejběžnějším) případě, kdy navazujeme "outlety" nějaké podtřídy UIViewControlleru na objekty uvnitř jeho hlavního rámce. Tyto objekty nám nezaniknou, protože si je "retainuje" hlavní rámec; ten sám je pak automaticky držen při životě knihovním kódem třídy UIViewController a nám zbývá pouze jediná povinnost – odkazy vynulovat v metodě viewDidUnload:
-(void)viewDidUnload {
[super viewDidUnload];
self.button=nil;
}
(A opět poznámka: díky deklaraci assign by zde byl v principu korektní i kód bez "self."; je ale vždy lepší používat přístupové metody, než sahat rovnou na instanční proměnné, vyjma v metodách init a dealloc.)
Metodu dealloc v tomto případě nepotřebujeme vůbec.
Naopak pro ostatní objekty – typicky kořenové rámce odlišné od hlavního apod. – je vhodné využít deklaraci s přepínačem retain; ta pak funguje stejně, jako výše uvedená instanční proměnná, tedy musíme implementovat uvolnění jak v metodě viewDidUnload (ale na rozdíl od instanční proměnné zde můžeme použít přístupovou metodu, tj. přesně stejně jako v minulém případě napíšeme jen "self.alternativeView=nil"), tak i v metodě dealloc.
Zde je podle firmy Apple (osobně s tím nesouhlasím) vhodnější přístupovou metodu nepoužívat pro risiko vedlejších efektů při případné reimplementaci v podtřídě; metoda by tedy mohla vypadat zhruba nějak takto:
@implementation MyController
@synthesize button=_button,alternativeView=_alternativeView;
...
-(void)dealloc {
[_alternativeView release],_alternativeView=nil;
[super dealloc];
}
@end
(I zde se vyplatí přičinit poznámku: předně, je vhodné název instanční proměnné od názvu atributu odlišit; použití podtržítka je běžnou konvencí a v tomto případě na něm není nic špatného. Dále pak, je vhodné po uvolnění instanční proměnnou vynulovat. Ani jedno není nutné; děláme-li to ale, podstatně snížíme risiko nepříjemných chyb.)
Kolekce outletů
Kolekce outletů mohou být deklarovány opět jako atributy nebo instanční proměnné; jejich typ musí být NSSet nebo NSArray. Ukažme si už jen variantu s atributem; přímá deklarace instanční proměnné by vypadala podobně (a atributy jsou beztak koncepčně čistší):
@interface MyController:UIViewController
@property (retain) IBOutletCollection(UILabel) NSSet *labels;
@end
V závorce za značkou IBOutletCollection uvádíme typ objektu, které pak bude v editoru XIBů možné do kolekce ukládat. Stejně jako tomu je u značky IBOutlet, i značku IBOutletCollection(...) překladač ignoruje; zpracuje ji ale Xcode a řídí podle ní funkci editoru XIBu, který odpovídající třídu obsahuje.
Je vhodné mít na paměti dvě drobnosti:
• v tomto případě nemá nikdy smysl deklarace s přepínačem assign – pole odkazů samo ovšem není součástí žádného rámce, a pokud bychom si je nepřidrželi přepínačem retain, bylo by automaticky uvolněno. Musíme je tedy také explicitně uvolnit v obou metodách viewDidUnload i dealloc;
• i pokud použijeme proměnnou typu NSArray, jsou v ní objekty fakticky neuspořádané (resp. jejich pořadí je "náhodné", speciálně neodpovídá pořadí, v němž kolekci outletů v XIBu naplníme).
Objekty je tedy třeba rozlišit jinak (je-li to vůbec zapotřebí), např. podle tagů.
Akce
Pro deklaraci akcí slouží standardní značka IBAction, již překladač interpretuje přesně stejně jako typ void, ale Xcode podle ní opět řídí vlastní editor XIBů:
@interface MyController:UIViewController
-(IBAction)buttonPressed:sender;
-(IBAction)anotherButton:(UIButton*)button;
-(IBAction)otherAction;
@end
Prvý z formátů s beztypovým (tedy typu id) argumentem sender je archetypální deklarace akce, s jakou se setkáme nejčastěji – ale jen málokdy je opravdu vhodná, používá se spíše ze setrvačnosti.
Praktičtější je druhá varianta, v níž argument opatříme odpovídajícím typem – funguje přesně stejně dobře, ale za prvé je "samodokumentováno", jaká třída bude tuto akci posílat, a také při implementaci můžeme používat atributy bez přetypování (např. "button.tag" – varianta "sender.tag" uvnitř implementace prvé z akcí by bohužel vinou nedomyšleného designu Objective C nebyla možná).
Konečně třetí varianta bez argumentu se hodí tam, kde nás odesílající nezajímá (obvykle proto, že jej stejně známe, a máme jej v některém z "outletů"). To je v praxi daleko nejběžnější případ; bohužel ale je v současnosti podporována pouze v iOSu a nikoli v Mac OS X.
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