V minulΘm Chipu jsme si ukßzali zßkladnφ chovßnφ vÜech objekt∙ ve v²vojovΘm prost°edφ Cocoa: podrobn∞ jsme si vysv∞tlili, jak a kdy objekty za asistence poloautomatickΘho garbage collectoru zanikajφ. Dnes se podφvßme na dv∞ paradigmata, je₧ zvyÜujφ efektivitu program∙ a zßrove≥ usnad≥ujφ jejich psanφ: jednφm z nich je koncepce m∞niteln²ch a nem∞nn²ch objekt∙, druh²m skrytΘ podt°φdy.
M∞nitelnΘ a nem∞nnΘ objekty
Zßkladnφ myÜlenkou koncepce m∞niteln²ch a nem∞nn²ch objekt∙ je dosa₧enφ vyÜÜφ efektivity, ani₧ by se o to programßtor musel v∞dom∞ starat. Typick²m p°φkladem je kopφrovßnφ objekt∙: v praxi pom∞rn∞ Φasto pot°ebujeme vytvo°it privßtnφ kopii objektu û jak²si jeho "snφmek", kter² uchovß momentßlnφ stav objektu i v p°φpad∞, ₧e se p∙vodnφ objekt zm∞nφ. P°edstavme si nap°φklad objekt, kter² reprezentuje haÜovacφ tabulku û takov² objekt v Cocoa skuteΦn∞ existuje a jmenuje se NSDictionary. Zßkladnφ dv∞ zprßvy, kterΘ je schopen zpracovat, jsou:
- (void)setObject:(id)anObject forKey:(id)aKey;
- (id)objectForKey:(id)aKey;
Prvnφ z nich ulo₧φ do tabulky dvojici <klφΦ, hodnota>, druhß vyhledß hodnotu k zadanΘmu klφΦi (v Φase nezßvisejφcφm na poΦtu hodnot v tabulce). Je z°ejmΘ, ₧e mß-li haÜovacφ tabulka b²t konsistentnφ, musφ intern∞ udr₧ovat ne odkazy na klφΦe, ale jejich nem∞nnΘ kopie û kdyby v tabulce byly jen odkazy na klφΦe, mohl by se objekt reprezentujφcφ klφΦ kdykoli zm∞nit, ani₧ by se o tom tabulka v∙bec "dozv∞d∞la"; haÜovacφ tabulka by v takovΘm p°φpad∞ byla samoz°ejm∞ nekorektnφ. Implementace metody setObject:forKey: tedy musφ vypadat p°ibli₧n∞ takto:
- (void)setObject:(id)anObject forKey:(id)aKey
{
id myKey=[aKey copy]; // pot°ebuji vlastnφ nem∞nnou kopii
id myVal=[anObject retain]; // hodnota se m∙₧e klidn∞ m∞nit (ale nesmφ zaniknout)
za°adit_do_tabulky(myKey,myVal);
}
Za t∞chto podmφnek bude haÜovacφ tabulka pracovat korektn∞, ovÜem zaplatφme za to zpomalenφm programu a v∞tÜφ spot°ebou pam∞ti: ka₧d² klφΦ vklßdan² do tabulky se musφ nejprve zkopφrovat û to znamenß, ₧e pot°ebujeme dvakrßt tolik pam∞ti a navφc program musφ kopφrovat data objektu. P°itom to v °ad∞ p°φpad∙ nenφ doopravdy zapot°ebφ: velmi Φasto (v praxi tΘm∞° v₧dy, proto₧e klφΦe obvykle b²vajφ textovΘ konstanty) se obsah klφΦ∙ stejn∞ nebude m∞nit. HaÜovacφ tabulka by si tedy mohla udr₧ovat pouze odkazy na klφΦe û musela by ale "v∞d∞t", kterΘ klφΦe se jeÜt∞ mohou m∞nit a kterΘ ne.
ObjektovΘ prost°edφ ale nabφzφ velmi elegantnφ °eÜenφ: haÜovacφ tabulka samoz°ejm∞ nem∙₧e v∞d∞t, kterΘ objekty se budou m∞nit; mohou to ale v∞d∞t tyto objekty samy! StaΦφ zavΘst pro ka₧d² typ objekt∙, pro kter² to dßvß rozumn² smysl, dv∞ t°φdy: t°φdu nem∞nn²ch objekt∙ a t°φdu objekt∙, kterΘ se mohou m∞nit û nap°φklad NSString (nem∞nnΘ) a NSMutableString (m∞nitelnΘ). Nem∞nnΘ objekty pak nemusejφ nikdy vytvß°et kopie û jejich metoda copy m∙₧e b²t implementovßna takto:
@implementation NSString
...
-copy
{
return [self retain];
}
...
@end
Nynφ funguje vÜe automaticky s nejvyÜÜφ mo₧nou efektivitou: vklßdßme-li do haÜovacφ tabulky klφΦ, kter² se nikdy nebude m∞nit, haÜovacφ tabulka bude udr₧ovat pouze odkaz û ₧ßdnß pam∞¥ navφc, nic se nekopφruje. Pouze v p°φpad∞, ₧e jako klφΦ vyu₧ijeme m∞niteln² objekt (nap°. NSMutableString), kopie se vytvo°φ; v takovΘm p°φpad∞ se tomu ale stejn∞ nem∙₧eme vyhnout. Navφc tent²₧ trik automaticky funguje nejen v haÜovacφ tabulce, ale kdekoli, kde pot°ebujeme okam₧itΘ kopie objekt∙. P°ipravujeme nap°φklad program, kter² si pro funkci undo musφ zapamatovat momentßlnφ stav sv²ch datov²ch objekt∙? Nic jednoduÜÜφho û prost∞ vytvo°φme kopie vÜech objekt∙ reprezentujφcφch data tak, ₧e jim poÜleme zprßvu copy. Dφky koncepci m∞niteln²ch a nem∞nn²ch objekt∙ nemusφme zkoumat, kterß data se mohou m∞nit a kterß ne û fakticky se zkopφrujφ jen ta, kter²ch se zm∞ny mohou t²kat.
Cocoa proto v °ad∞ p°φpad∙ nabφzφ dvojice t°φd NSXXX a NSMutableXXX, kde objekty t°φdy NSXXX se nemohou m∞nit, zatφmco objekty t°φdy NSMutableXXX ano (je tomu tak mimochodem i u t°φdy NSDictionary û metoda setObject:forKey: je tedy samoz°ejm∞ k dispozici pouze u objekt∙ t°φdy NSMutableDictionary). T°φda NSMutableXXX je v₧dy d∞dicem t°φdy NSXXX; m∞nitelnΘ objekty tedy "um∞jφ" vÜechno, co nem∞nnΘ, a navφc jsou schopny zm∞n. PoÜleme-li kterΘmukoli objektu t°φdy NSXXX zprßvu copy, nevytvo°φ se ₧ßdnß kopie; namφsto toho zφskßme dalÜφ odkaz na tent²₧ (nem∞nn²) objekt. PoÜleme-li vÜak zprßvu copy objektu t°φdy NSMutableXXX, dostaneme nov² objekt t°φdy NSXXX, kter² bude obsahovat nem∞nnou kopii momentßlnφho stavu p∙vodnφho objektu.
Uv∞domme si, ₧e koncepce m∞niteln²ch a nem∞nn²ch objekt∙ zaruΦuje co nejefektivn∞jÜφ zkopφrovßnφ i u slo₧en²ch objekt∙. Jako p°φklad vezm∞me objekt t°φdy NSMutableArray, kter² reprezentuje pole libovoln²ch dalÜφch objekt∙, do n∞j₧ m∙₧eme p°idßvat nebo z n∞j odebφrat (odpovφdajφcφ nem∞nnß t°φda NSArray reprezentuje pole, jeho₧ obsah nem∙₧eme m∞nit). Obr. 1 ukazuje p°φklad objektu t°φdy NSMutableArray, obsahujφcφho (odkazy na) jak m∞nitelnΘ, tak nem∞nnΘ objekty. Vy₧ßdßme-li si nynφ zprßvou copy nem∞nnou kopii momentßlnφho stavu tohoto objektu, musφ se vytvo°it nov² objekt t°φdy NSArray (proto₧e existujφcφ objekt mutableArray je m∞niteln²) se stejn²m (a nem∞nn²m) obsahem. Nov² objekt tedy m∙₧e se star²m sdφlet odkazy na nem∞nnΘ vno°enΘ objekty, ale musφ obsahovat vlastnφ (nem∞nnΘ) kopie objekt∙, kterΘ byly m∞nitelnΘ. V²sledek vidφme na obr. 2.
╚as od Φasu bychom mohli pot°ebovat "p°ece jen" zm∞nit nem∞nn² objekt. Doslova to samoz°ejm∞ nenφ mo₧nΘ û tφm bychom celou koncepci m∞niteln²ch a nem∞nn²ch objekt∙ postavili na hlavu. M∙₧eme si vÜak pomocφ zprßvy mutableCopy vy₧ßdat vytvo°enφ m∞nitelnΘ kopie objektu. Obsahuje-li p∙vodnφ objekt vno°enΘ objekty, bude jeho m∞nitelnß kopie obsahovat (odkazy na) tytΘ₧ objekty, a to i v p°φpad∞, ₧e tyto objekty samy jsou nem∞nnΘ (chceme-li nap°. vytvo°it m∞nitelnou kopii pole, je to proto, abychom do n∞j mohli p°idßvat nebo z n∞j odebφrat dalÜφ objekty; ne proto, abychom mohli m∞nit objekty v n∞m obsa₧enΘ). V²sledek vytvo°enφ m∞nitelnΘ kopie pole z minulΘho p°φkladu ukazuje obr. 3.
Koncepce m∞niteln²ch a nem∞nn²ch objekt∙ je velmi siln²m a Üikovn²m mechanismem, kter² krom∞ v²raznΘho zv²Üenφ efektivity program∙ dokß₧e i omezit programßtorskΘ chyby. Pou₧φvßme-li nem∞nn² objekt, nem∙₧e se nßm omylem stßt, ₧e jej n∞kter² ·sek programu zm∞nφ (z podobnΘho d∙vodu byl nap°. v ANSI C zaveden modifikßtor const). Rozd∞lenφ m∞niteln²ch a nem∞nn²ch objekt∙ na samostatnΘ t°φdy NSXXX a NSMutableXXX navφc umo₧≥uje n∞kterΘ takovΘ chyby odchytit ji₧ p°i p°ekladu û pokusφme-li se nap°φklad staticky typovanΘmu objektu t°φdy NSArray poslat zprßvu addObject:, p°ekladaΦ vydß varovßnφ.
SkrytΘ podt°φdy
Zatφmco koncepce m∞niteln²ch a nem∞nn²ch objekt∙ trochu zkomplikovala programßtorskΘ rozhranφ Cocoa (namφsto jedinΘ t°φdy nap°. NSString mßme dv∞ û NSString a NSMutableString) pro zajiÜt∞nφ v∞tÜφ efektivity a v∞tÜφ robustnosti, je hlavnφm ·Φelem koncepce skryt²ch podt°φd programßtorskΘ rozhranφ bez ztrßty efektivity co nejvφce zjednoduÜit (nebo naopak û p°i zachovßnφ jednoduchΘho a p°ehlednΘho API dosßhnout maximßlnφ efektivity).
Koncepci skryt²ch podt°φd si op∞t ukß₧eme na p°φkladu. Dejme tomu, ₧e chceme vytvo°it t°φdu, jejφ₧ instance by reprezentovaly Φφsla (takovß t°φda je souΦßstφ Cocoa a jmenuje se NSNumber). Pokud bychom nevyu₧ili koncepce skryt²ch podt°φd, mßme v podstat∞ dv∞ mo₧nosti:
1. Vytvo°φme t°φdu NSNumber, kterß bude sama o sob∞ schopna pracovat s jak²mkoli typem Φφsla (char, int, unsigned, long, 64 bit∙, float...). To je samoz°ejm∞ mo₧nΘ, ale tento p°φstup mß dv∞ nev²hody: naprogramovßnφ takovΘ komplikovanΘ t°φdy je slo₧itΘ, snadno se p°i n∞m ud∞lß chyba a slo₧it² zdrojov² k≤d se Üpatn∞ udr₧uje. Druhou (a mo₧nß zßva₧n∞jÜφ) nev²hodou je, ₧e implementace takovΘ t°φdy nenφ efektivnφ, proto₧e musφ zahrnovat pot°eby vÜech Φφseln²ch typ∙ a nem∙₧e b²t optimalizovßna pro pot°eby jednoho konkrΘtnφho typu.
2. T°φda NSNumber sama bude pouze abstraktnφ nadt°φdou, shrnujφcφ obecnΘ vlastnosti vÜech Φφsel, a skuteΦn²mi reprezentanty jednotliv²ch typ∙ budou jejφ podt°φdy û asi tak, jak naznaΦuje obr. 4. To je lepÜφ, skuteΦn∞ objektovΘ °eÜenφ û ka₧dß z podt°φd je jednoduchß, snadno udr₧ovatelnß a snadno m∙₧e b²t takΘ maximßln∞ optimalizovßna. Nep°φjemnou nev²hodou vÜak je velmi komplikovanΘ programßtorskΘ rozhranφ û programßtor si musφ pamatovat jakΘsi t°φdy NSCharNumber, NSUnsignedCharNumber... a musφ se sßm starat o to, aby se pou₧ila pot°ebnß t°φda. To je nepohodlnΘ a v objektovΘm prost°edφ je to dokonale zbyteΦnΘ.
Koncepce skryt²ch podt°φd je jednoduchouΦk² a p°itom nesmφrn∞ efektivnφ trik: vlastn∞ se vyu₧φvß implementace podle bodu 2, ale API programßtor∙m nabφzφ pouze rozhranφ podle bodu 1. Programßtor tedy vyu₧φvß v₧dy jen a pouze slu₧eb t°φdy NSNumber a jejφ podt°φdy v∙bec neznß (jejich konkrΘtnφ poΦet a druhy dokonce v∙bec nejsou souΦßstφ API a snadno se mohou m∞nit mezi jednotliv²mi verzemi systΘmu, bez jak²chkoli zßporn²ch d∙sledk∙ pro kompatibilitu program∙). T°φda NSNumber sama p°i vytvß°enφ objektu rozhodne, kterß z jejφch (skryt²ch) podt°φd je pro danΘ Φφslo optimßlnφ, a vytvo°φ odpovφdajφcφ objekt; i s nφm programßtor komunikuje jako s objektem t°φdy NSNumber (co₧ je v naprostΘm po°ßdku, proto₧e objekt je d∞dicem t°φdy NSNumber). Tuto situaci ilustruje poslednφ, pßt² obrßzek.
Vytvo°φme-li tedy n∞kolik "instancφ t°φdy NSNumber" takto:
m∙₧e b²t ve skuteΦnosti ka₧d² z nov∞ vytvo°en²ch objekt∙ instancφ jinΘ t°φdy. VÜechny vÜak jsou d∞dici t°φdy NSNumber a jako s takov²mi s nimi m∙₧eme pracovat.
Stojφ za to si uv∞domit, ₧e toto skv∞lΘ °eÜenφ je v jazycφch typu C++ trochu problematickΘ: jde o Üpatn∞ navr₧en² systΘm tvorby objekt∙ û konstrukce "new NSNumber" v C++ prost∞ nem∙₧e vytvo°it objekt jinΘ t°φdy ne₧ prßv∞ t°φdy NSNumber. SkrytΘ podt°φdy zde nelze pou₧φt (je mo₧nΘ to do jistΘ mφry dohnat pomocnou statickou metodou, tam vÜak zase nastanou problΘmy s nemo₧nostφ tyto metody d∞dit). Tuto neÜ¥astnou koncepci z C++ bohu₧el do znaΦnΘ mφry p°ebral i jinak velmi dobr² objektov² jazyk Java.
Cocoa vyu₧φvß koncepce skryt²ch podt°φd velmi Φasto. Prßv∞ dφky tomu je API Cocoa mnohem jednoduÜÜφ a p°ehledn∞jÜφ ne₧ nap°φklad "C++kovΘ" API operaΦnφho systΘmu Epoc, p°esto₧e slu₧by Epocu nabφzejφ jen zlomek luxusu a flexibility slu₧eb Cocoa. Typick²m p°φkladem skryt²ch podt°φd jsou prakticky vÜechny t°φdy Foundation Kitu, kterΘ reprezentujφ slo₧enΘ objekty (jako NSArray nebo NSDictionary) û ty vyu₧φvajφ skryt²ch podt°φd pro volbu optimßlnφ implementace z hlediska pom∞ru efektivity a pam∞¥ovΘ nßroΦnosti, ani₧ by se tφm musel programßtor explicitn∞ zab²vat. Programßtor nadto samoz°ejm∞ m∙₧e v p°φpad∞ pot°eby snadno sßm doplnit dalÜφ skrytΘ podt°φdy pro rozÜφ°enφ slu₧eb celΘ skupiny t°φd.
Shrnutφ
DokonΦili jsme p°ehled zßkladnφch vlastnostφ objekt∙ Cocoa p°edevÜφm z hlediska doby jejich ₧ivota; ji₧ vφme, kdy a jak objekty v systΘmu Cocoa zanikajφ. Dnes jsme se navφc seznßmili s n∞kter²mi dalÜφmi paradigmaty, je₧ zajiÜ¥ujφ vysokou efektivitu p°i udr₧enφ jednoduchosti a p°ehlednosti API.
P°φÜt∞ si zb∞₧n∞ ukß₧eme konkrΘtnφ prost°edφ pro programßtorskou prßci, je₧ Cocoa nabφzφ û aplikaci ProjectBuilder.