Objective C

Principy objektového programování samozřejmě můžeme používat v jakémkoli programovacím jazyce; v některém to však jde snáze, v jiném hůř -- podobně, jako strukturovaně lze programovat v COBOLu i v PASCALu, v prvním případě to však dá dost práce. Dnes existuje řada jazyků, navržených přímo pro podporu objektově orientovaného programování; jedním z nich -- nadstavbou jazyka C, nazvanou Objective C -- se budeme zabývat v tomto článku. Nebudeme popisovat programovací jazyk C, ale zaměříme se pouze na rozšíření Objective C; pojmy jako 'objekt', 'třída', 'zpráva' nebo 'metoda' budeme používat bez podrobnějšího vysvětlení ve smyslu, ve kterém jsou definovány v úvodním článku 'Objektově orientovaná analýza a design'.

Měl bych snad začít alespoň orientačním srovnáním Objective C s daleko známějším C++. Objective C je doslova a do písmene objektová nadstavba jazyka C; nezavádí (téměř) žádná rozšíření kromě vlastního aparátu pro práci s objekty. Ten je postaven na klasickém modelu předávání zpráv ve stylu SmallTalku; syntaxe zpráv v Objective C je dokonce přímo převzata ze SmallTalku. Objective C je plně objektový systém využívající v plné míře pozdní vazbu, vyžaduje proto netriviální runtime podporu pro dynamickou komunikaci mezi objekty -- v ideálním případě podporovanou na úrovni operačního systému. Snad proto se s jeho implementacemi setkáváme daleko řidčeji. C++ naproti tomu není objektovým jazykem; jedná se o sadu vylepšení a doplňků k jazyku C, které jaksi mimochodem umožňují i (nepříliš pohodlnou) tvorbu programů metodami OOP. Takto vytvořené programy již nevyžadují objektový runtime systém, a mohou být proto bez problémů využívány v principiálně neobjektovém prostředí jakým je třeba MS DOS nebo MS Windows.



1. Neobjektová rozšíření

Objective C zachovává jednu z největších výhod původního jazyka C -- jeho jednoduchost. Nezavádí vůbec žádné nové příkazy; pouze několik direktiv a typů potřebných pro OOP a jednu novou jazykovou konstrukci -- odeslání zprávy zadanému objektu. To samozřejmě přináší řadu výhod:

zkušený programátor v C se Objective C může naučit doslova přes víkend;
překladač Objective C je jen o málo složitější než překladač jazyka C;
programy psané v C lze až na nepatrné výjimky bez problémů překládat překladačem Objective C

Jazyková rozšíření Objective C nesouvisející s objekty jsou pouze tři:



1.1. komentář '//'

Objective C doplňuje do jazyka C komentář uvozený dvěma lomítky a ukončený koncem řádky (stejné komentáře podporuje i C++).



1.2. typ BOOL

To, že jazyk C nemá speciální typ pro logické hodnoty, je jednou z jeho zásadních výhod; může to však přinášet problémy při spojování programů, které používají fakticky různé typy pro tyto hodnoty a různé hodnoty pro 'pravdu'. Objective C proto zavádí typ BOOL a odpovídající hodnoty YES a NO. V 'obyčejném C' bychom mohli téhož dosáhnout například definicí

typedef enum {NO, YES} BOOL;



1.3. direktiva #import

Standardní direktiva #include pro vložení jiného souboru má jednu nepříjemnou nevýhodu -- umožňuje vložit jeden a tentýž soubor několikrát. Uvedeme-li tedy na začátku programu direktivy

#include <stdarg.h>
#include <stdio.h>

a obsahuje-li soubor stdio.h direktivu '#include <stdarg.h>', bude se soubor stdarg.h překládat dvakrát. V 'obyčejném C' se problém řeší poměrně nepohodlnými konstrukcemi typu

#ifndef _STDIO_H_
#define _STDIO_H_
...
#endif

v Objective C si můžeme ušetřit práci využitím direktivy #import namísto starého #include; překladač pak zajistí, že každý zdrojový soubor se bude překládat nejvýše jednou.



2. Nové typy a hodnoty

Kromě typu BOOL, který již známe, zavádí Objective C několik dalších typů, umožňujících práci s objekty:

id objekt
Class třída
SEL zpráva
IMP metoda (přímý ukazatel na metodu, používaný pro statický přístup)

Základním novým typem je samozřejmě id, reprezentující objekt. Ve skutečnosti se nejedná o nic jiného, než o ukazatel na data, která objekt v paměti reprezentují, a je definován jako void*. S ostatními typy se setkáme poměrně zřídkakdy: typ Class je doplněn pouze pro lepší přehlednost, protože třída je v Objective C objekt jako každý jiný a Class je tedy zcela ekvivalentní typu id. Typ SEL reprezentuje zprávu a je definován jako celočíselná hodnota, na kterou je zpráva interně přeložena. Konečně typ IMP  vlastně není ničím jiným, než ukazatelem na funkci.

V souvislosti s novými typy nabízí Objective C i dvě nové hodnoty: nil je hodnota typu id a reprezentuje neexistující objekt, podobně Nil je hodnota typu Class a reprezentuje neexistující třídu. Obě nové hodnoty jsou zavedeny jen pro zvýšení čitelnosti programu a jsou zcela ekvivalentní 'obyčejnému' NULL.



3. Standardní identifikátory

Objective C nepřidává k jazyku C žádné nové klíčové slovo a definuje pouze tři standardní identifikátory self, super a _cmd. První z nich v implementaci metody reprezentuje objekt, který metodu zpracovává, druhý také, ale jeho metody jsou vyhledávány v rodičovské třídě. Třetí identifikátor reprezentuje zpracovávanou zprávu; využívá se pro rozlišení v případech kdy je několik zpráv zpracováváno v rámci jedné metody.

Díky tomu že self, super a _cmd jsou identifikátory a ne klíčová slova, můžeme je bez jakýchkoli problémů předefinovat; překladač Objective C proto bez problémů přeloží 'obyčejný céčkový' program, ve kterém je použita např. proměnná jménem self.



4. Direktivy

Prakticky všechny 'novinky', které Objective C přináší, jsou reprezentovány direktivami syntakticky podobnými direktivám preprocesoru, uvedenými ale znakem '@' namísto znaku '#'. Direktivy můžeme rozdělit do tří skupin:



4.1. Deklarace nových tříd

@interface
...
@end

Mezi direktivami @interface a @end je popsáno tzv. interface třídy. To je tvořeno deklaracemi proměnných, které reprezentují objekt deklarované třídy, a deklaracemi metod, které třída nabízí. Deklarace proměnných v podstatě nejsou ničím jiným, než starou známou deklarací struktury (pouze bez klíčového slova struct); deklarace metod jsou pak velmi podobné hlavičkám funkcí, jen mají mírně pozměněnou syntaxi kvůli přiblížení zprávám.

@implementation
...
@end

Direktivy @implementation ... @end obsahují tzv. implementaci třídy, tj. zdrojový kód jejích metod.

@protocol
...
@end

Direktiva @protocol uvádí deklaraci tzv. protokolu. Protokol v zásadě není ničím jiným, než seznamem metod; používá se jako společný prvek pro specifikaci tříd, které mají mít společné metody, ale nejsou strukturálně příbuzné (čímž nahrazuje implementačně i programátorsky obtížnou vícenásobnou dědičnost C++ v tom jediném případě, kdy měla jakýsi smysl).



4.2. Přístup k proměnným

Proměnné objektu mohou být k dispozici pouze jeho vlastním metodám, nebo i metodám všech jeho dědiců, nebo -- ve vyjímečných případech, kdy z nějakého důvodu musíme rezignovat na OOP a využívat statické programátorské techniky -- mohou být proměnné přístupné z jakéhokoli úseku kódu. Možnosti přístupu k proměnným jsou určeny použitím jedné ze tří direktiv:

@private proměnné jsou přístupné pouze metodám objektu samotného;
@protected proměnné jsou přístupné i dědicům (tento přístup je standardní nepoužijeme-li žádnou z direktiv);
@public proměnné jsou přístupné komukoli.



4.3. Ostatní direktivy

Objective C nabízí několik dalších, nepříliš často využívaných direktiv:

@class deklarace jména třídy, definované jinde;
@selector() převod symbolického jména zprávy na typ SEL;
@protocol() vyhledání protokolu zadaného jménem;
@encode() převod symbolického jména typu na jeho znakovou reprezentaci;
@defs() deklarace proměnných zadané třídy (pro statický přístup).

Za zvláštní zmínku stojí direktiva @encode, která umožňuje dynamickou práci s typy. Standardní jazyk C sice umožňuje předávat např. jako parametr funkce hodnotu typu, určeného až při běhu programu (např. využitím ukazatele void*), nenabízí však žádnou standardní možnost, jak funkci předat informaci o tom, o jaký typ se jedná. V Objective C můžeme pohodlně využít direktivu @encode, která libovolný typ kóduje do řetězce znaků; ten pak můžeme uvnitř funkce snadno dekódovat. V tomto článku není dostatek místa na podrobný popis kódování; uvedeme proto pouze jediný jednoduchý příklad: typ 'int *[12]' bude reprezentován řetězcem "[12^i]".



5. Zprávy

Objective C zavádí novou jazykovou kostrukci pro předání zprávy objektu; tato kostrukce je výrazem (a může tedy samozřejmě stát kdekoli, kde smíme uvést nějaký výraz -- třeba v inicializační části příkazu for). Syntaxe je velmi jednoduchá:

[příjemce zpráva]

kde 'příjemce' je libovolný výraz typu id (nebo typu Class, který je s ním ekvivalentní) a určuje objekt, který má zprávu dostat. 'Zpráva' pak má syntaxi převzanou ze SmallTalku -- její identifikátor může obsahovat jednu či více dvojteček, každá dvojtečka reprezentuje jeden parametr. Uveďme několik příkladů zpráv: simpleMessage je zpráva bez parametru, setValueTo:10 je zpráva s jedním -- v tomto případě celočíselným -- parametrem a konečně třeba zpráva insertString:"Vložit" toString:"Semka><to přijde" toPosition:6 je příklad zprávy se třemi parametry. Povšimněme si, že díky vkládání parametrů přímo do identifikátoru zprávy je celý výraz daleko čitelnější a srozumitelnější, než kdybychom použili klasickou 'céčkovou' syntaxi s předáváním parametrů v závorkách za jménem zprávy.



6. Využití, příklady

Mikroskopický rozsah tohoto článku neumožňuje podrobně popsat způsoby využití konstrukcí, které Objective C pro objektové programování nabízí. Nemůžeme bohužel ani shrnout služby standardní třídy Object, která je obvykle využívána jako univerzální rodičovská třída a která díky dědičnosti dává libovolnému objektu širokou paletu velmi univerzálních schopností. Čtenář se proto zatím musí spokojit s několika bohatě komentovanými příklady jednoduchých programů, které doprovázejí tento článek; s výjimkou příkladů 1 a 7, které ukazují využití hotové knihovní třídy, nevyžadují příklady nic jiného než překladač Objective C se standardními knihovnami.

Příklad 1: využití předdefinovaných tříd (knihovny tříd NeXTstepu)
Příklad 2: tvorba vlastní třídy
Příklad 3: dědičnost a vkládání objektů
Příklad 4: dynamické rozpoznání třídy
Příklad 5: skládání objektů a dynamické rozpoznání zpráv
Příklad 6: skládání objektů a dynamické rozpoznání zpráv
Příklad 7: mechanismus klient/server
Příklad 8: statický přístup k objektům



(ostatní články)


Copyright (c) Ondra Čada