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:
Informace
Sledování změn objektů: přímý přístup
4. dubna 2006, 00.00 | Co dělat v případě, kdy z jakýchkoli důvodů potřebujeme přímo měnit obsah některé z proměnných, tedy kdy nechceme nebo nemůžeme použít set-accessor? Ve složitějších případech, nebo tehdy, když potřebujeme dosáhnout co největší efektivity, můžeme také automatické posílání notifikací KVO zcela vypnout.
Připomeňme, že minule jsme si ukázali, jak správně implementovat modelové třídy – většinou se nemusíme starat zhola o nic, někdy musíme jen ještě implementovat třídní metodu initialize, jež pomocí standardní zprávy setKeys:triggerChangeNotificationsForDependentKey: nastaví správně závislosti odvozených atributů na ostatních.
Hlášení změn proměnných
Jednu drobnost jsme ale nechali otevřenou: co dělat v případě, kdy z jakýchkoli důvodů potřebujeme přímo měnit obsah některé z proměnných, tedy kdy nechceme nebo nemůžeme použít set-accessor? Typickým případem může být situace, kdy dva accessory oba mění tytéž proměnné – pokud bychom tedy v obou volali pro nastavení accessor, došlo by k zacyklení.
Nejjednodušším příkladem může být třeba třída obsahující atributy width a height, jež spolu mají zůstávat v poměru, odpovídajícím poměru stran televizní obrazovky – pokud by nám nezáleželo na podpoře KVO, mohli bychom accessory naprogramovat třeba takto:
@interface Test:NSObject { float width,height; } @end @implementation Test -(void)setWidth:(float)w { height=(width=w)*3./4.; } -(void)setHeight:(float)h { width=(height=h)*4./3.; } @end
Pro podporu KVO musíme nějak "říci" systému, že měníme hodnotu některé z proměnných objektu. API KVO k tomu nabízí standardní zprávy willChangeValueForKey: a didChangeValueForKey:. Prvou z nich musíme odeslat předtím, než k jakékoli změně dojde (systém KVO si v ní totiž ukládá "starou" hodnotu proměnné, již mohou potřebovat někteří z příjemců notifikací KVO); druhou pak pošleme ve chvíli, kdy jsou změny ukončeny – a systém KVO v jejím rámci rovnou pošle odpovídající notifikaci:
-(void)setWidth:(float)w { [self willChangeValueForKey:@"height"]; height=(width=w)*3./4.; [self didChangeValueForKey:@"height"]; } -(void)setHeight:(float)h { [self willChangeValueForKey:@"width"]; width=(height=h)*4./3.; [self didChangeValueForKey:@"width"]; }
Efektivnější, než nastavení na úrovni třídy
Pozorní čtenáři minulého dílu ovšem mohou namítnout, že namísto uvedeného kódu bychom se stejně dobře obešli s použitím KVO nepodporujících accessorů z prvého příkladu, pokud bychom ve třídě ještě navíc použili kód
+(void)initialize { [self setKeys:[NSArray arrayWithObject:@"width"] triggerChangeNotificationsForDependentKey:@"height"]; [self setKeys:[NSArray arrayWithObject:@"height"] triggerChangeNotificationsForDependentKey:@"width"]; }
– a bylo by to možná i méně práce. To je pravda – v našem triviálním příkladu; v praxi, kdy různé metody mohou měnit různé proměnné, nadto ještě pouze při splnění různých podmínek, však je přímé použití zpráv willChangeValueForKey: a didChangeValueForKey: pohodlnější a také daleko efektivnější, neboť je můžeme volat také podmíněně, kdežto notifikace na základě nastavení zprávou setKeys:triggerChangeNotificationsForDependentKey: se posílají vždy:
-(void)setFoo:foo { ... if (...) { [self willChangeValueForKey:@"a"]; a=...; [self didChangeValueForKey:@"a"]; if (...) { [self willChangeValueForKey:@"b"]; [self willChangeValueForKey:@"c"]; [self willChangeValueForKey:@"d"]; b=...; c=...; d=...; [self didChangeValueForKey:@"d"]; [self didChangeValueForKey:@"c"]; [self didChangeValueForKey:@"b"]; } } ... }
Mimochodem, povšimněte si pořadí zpráv ve druhém příkazu if – to je důležité: pokud "otevřeme" změnu více atributů najednou pomocí několika zpráv willChangeValueForKey:, musíme ji "uzavřít" odpovídajícími zprávami didChangeValueForKey: v přesně opačném pořadí (jinak by za určitých okolností mohlo dojít k deadlocku).
Speciální zprávy pro relace 1:N
Podobně, jako systém KVC umožňoval dosáhnout vyšší efektivity využitím speciálních zpráv pro relace 1:N, zvláštní zprávy pro tento případ nabízí i KVO – to proto, že pokud řekneme pouze "pole se mění", je to příliš hrubá informace, a automatické zjišťování toho jak se změnilo je poměrně problematické a neefektivní. Namísto toho tedy nabízí KVO speciální zprávy ...will- a ...didChange, jejichž prostřednictvím můžeme přímo určit typ změny, kterou provádíme.
Pro setříděné relace 1:N, které jsou obvykle (ale jak víme z KVC, samozřejmě ne nutně) representovány objektem třídy NSArray, jsou určeny zprávy
-willChange:change valuesAtIndexes:indexes forKey:key; -didChange:change valuesAtIndexes:indexes forKey:key;
kde argument change určuje typ změny a může nabývat hodnot NSKeyValueChangeInsertion, NSKeyValueChangeRemoval či NSKeyValueChangeReplacement pro ohlášení vložení nových objektů, odstranění existujících nebo jejich změnu. Objekty jsou identifikovány prostřednictvím seznamu indexů indexes, uloženého v objektu třídy NSIndexSet.
Podobně pak pro nesetříděné relace 1:N, typicky (ale ne nutně) representované objekty třídy NSSet, jsou k dispozici standardní zprávy
-didChangeValueForKey:key withSetMutation:kind usingObjects:objects; -didChangeValueForKey:key withSetMutation:kind usingObjects:objects;
Zde pak argument objects typu NSSet obsahuje přímo měněné objekty, kdežto výčtová hodnota kind říká, k jaké změně dochází: NSKeyValueUnionSetMutation informuje o vložení nových objektů, NSKeyValueMinusSetMutation naopak o odebrání objektů existujících; podobně slouží i NSKeyValueIntersectSetMutation, kdy je jediný rozdíl v tom, že argument objects neobsahuje objekty odebírané, nýbrž naopak ty, jež zůstávají. Konečně pak NSKeyValueSetSetMutation indikuje, že se obsah zcela mění.
Automatiku můžeme také vypnout
Ve složitějších případech, nebo tehdy, když potřebujeme dosáhnout co největší efektivity, můžeme také automatické posílání notifikací KVO zcela vypnout – stačí implementovat třídní metodu automaticallyNotifiesObserversForKey: , jejímž prostřednictvím systém KVO zjišťuje, pro které atributy má automaticky notifikace posílat. V rámci její implementace pak prostě vrátíme hodnotu NO pro atributy, pro něž automatické odesílání notifikací nechceme – vypnutí pro atributy width a height z úvodního příkladu by tedy mohlo vypadat například takto:
+(BOOL)automaticallyNotifiesObserversForKey:(NSString*)key { if ([key isEqualToString:@"width"]) return NO; if ([key isEqualToString:@"height"]) return NO; return [super automaticallyNotifiesObserversForKey:key]; }
Samozřejmě pak musíme všechny notifikace odesílat explicitně – opět pomocí týchž zpráv willChangeValueForKey: a didChangeValueForKey: (respektive jejich speciálních variant pro relace 1:N), s nimiž jsme se seznámili v minulém odstavci – tentokrát je musíme poslat i pro tu proměnnou, jíž accessor patří:
-(void)setWidth:(float)w { [self willChangeValueForKey:@"width"]; [self willChangeValueForKey:@"height"]; height=(width=w)*3./4.; [self didChangeValueForKey:@"height"]; [self didChangeValueForKey:@"width"]; } -(void)setHeight:(float)h { [self willChangeValueForKey:@"width"]; [self willChangeValueForKey:@"height"]; width=(height=h)*4./3.; [self didChangeValueForKey:@"height"]; [self didChangeValueForKey:@"width"]; }
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