Zpět Obsah Další

Objective C: příklad 5
Skládání objektů a dynamické rozpoznání zpráv


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_1Class_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

1. Interface

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.

2. Inicializace

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.

3. Uvolnění objektu

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.).

4. Dynamické rozpoznání zpráv

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á..

5. Přesměrování zpráv

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 © Chip, O. Čada 2000-2003