Zpět | Obsah | Další |
V pátém příkladu se seznámíme s komplikovanějším mechanismem, který v Objective C umožňuje dynamické rozpoznávání zpráv: při implementaci třídy můžeme sami určit kód, který bude přidělovat každé zprávě metodu, která ji má zpracovat! Zároveň si ukážeme další mechanismus, který Objective C nabízí: jedná se o přesměrování zpráv -- objekt si může zpracování zprávy vyžádat od jiného objektu.
Příklad bude odpovídat vícenásobné dědičnosti, známé z jiných jazyků. Vytvoříme novou třídu, která bude "dědit" po skupině tříd Class_1 až Class_N; toto dědění zajistíme tak, že objekt naší třídy si vytvoří objekty všech vnořených tříd a dostane-li nějakou zprávu, předá ji ke zpracování některému z těchto vložených objektů.
// Objective C -- příklad 5
//
// ukázka skládání objektů a dynamického rozpoznávání zpráv
// pro simulaci vícenásobné dědičnosti
// (tento příklad není úplným programem)
// importujeme interface všech tříd, po kterých chceme dědit
#import "Class_1.h"
...
#import "Class_N.h"
#define NCLASSES
N
// počet děděných tříd (pro jednoduchost statický)
@interface MultipleInheritance: NSObject
{
id class[NCLASSES]; // pole vložených objektů
}
// předpokládejme, že nová třída nemá žádné vlastní metody, pouze ty zděděné
@end
@implementation MultipleInheritance
// musíme reimplementovat metodu init pro inicializaci vložených objektů
-init
{
[super init];
// zde můžeme snadno určit pořadí alokace objektů
class[0]=[[Class_1 class1] retain];
...
class[N-1]=[[Class_N classN] retain];
return self;
}
// analogicky musíme reimplementovat metodu dealloc pro jejich uvolnění
-(void)dealloc
{
int i;
// zde můžeme snadno určit pořadí uvolňování objektů
for (i=0;i<NCLASSES;i++)
[class[i] release];
[super dealloc];
}
-(void)forwardInvocation:(NSInvocation*)inv
{
int i;
for (i=0;i<NCLASSES;i++)
// je-li vložený objekt schopen zprávu zpracovat ...
if ([class[i] respondsToSelector:[inv selector]]) // ... předáme mu ji!
[inv invokeWithTarget:class[i]];
// žádný z vložených objektů zprávu nemůže zpracovat ...
// mohli bychom třeba ohlásit chybu, nebo zprávu ignorovat:
}
@end
// end of file
Properties objektu obsahují pole odkazů na vnořené objekty. Stejně jako v případě skutečné vícenásobné dědičnosti je zde pole statické; povšimněme si však, že bychom mohli snadno využít dynamické pole, alokované při inicializaci objektu -- tedy něco, co již vícenásobná dědičnost není schopna zajistit. A to jsme teprve začali...
Nedefinujeme žádné metody, protože objekt žádné vlastní metody neimplementuje: reimplementované metody init a dealloc dědí po třídě NSObject a všechny ostatní metody přesměruje na vložené objekty.
V rámci metody init prostě vytvoříme všechny vložené objekty a jejich identifikace uložíme do pole class. Uvědomme si, že můžeme libovolně změnit pořadí vytváření (a tedy i inicializace) jednotlivých vložených objektů a že můžeme při vytváření kteréhokoli z nich určit jakékoli potřebné parametry -- i to bývá při využití klasické vícenásobné dědičnosti v C++ či podobném nedomyšleném jazyku často neřešitelný problém.
Podobně jako metodu init musíme reimplementovat i metodu dealloc -- ta nejprve uvolní všechny vložené objekty a teprve nakonec voláním [super dealloc] objekt uvolní sám sebe. Opět můžeme snadno změnit pořadí uvolňování jednotlivých objektů a/nebo můžeme před uvolněním objektu ještě provést jakoukoli jeho potřebnou deinicializaci (uložení dat do souboru apod.).
Pro dynamické rozpoznání zpráv nabízí Objective C zprávu forwardInvocation:. Ta funguje jednoduchým způsobem: dostane-li kterýkoli objekt zprávu, kterou není schopen zpracovat, pošle mu runtime systém Objective C automaticky zprávu forwardInvocation:; v jejím rámci může objekt zprávu zpracovat dynamicky. Parametrem zprávy je speciální objekt třídy NSInvocation, který representuje kompletní zprávu včetně všech jejích parametrů, a který ji dokáže podle potřeby odeslat.
Implementace zprávy forwardInvocation: je v našem případě velmi jednoduchá: postupně se optáme všech vloženým objektů jsou-li schopny zprávu zpracovat -- využijeme k tomu standardní zprávu respondsToSelector:, se kterou jsme se seznámili již v minulém příkladu. Selektor přijaté zprávy zná -- stejně jako všechny ostatní údaje -- objekt NSInvocation. Jakmile narazíme na objekt, který zprávu zpracovat umí, přesměrujeme ji.
Je zřejmé, že bychom mohli algoritmus vyhledávání objektu, který bude zprávu zpracovávat, libovolně obměňovat: mohli bychom měnit pořadí zkoumaných objektů, mohli bychom např. vždy preferovat objekt, který zpracoval minulou zprávu a podobně. O něčem takovém se vícenásobné dědičnosti ani nezdá..
Pro přesměrování zpráv použijeme službu invokeWithTarget: objektu NSInvocation -- stačí mu říci, kterému objektu má zprávu předat, a to je vše.
Zpět | Obsah | Další |
Copyright (c) Chip, O. Čada 2000