Zpět Obsah Další

Foundation Kit: konkrétní třídy 3


Ukážeme si několik dalších příkladů konkrétního využití tříd Foundation Kitu. Řadu tříd z přehledu prozatím přeskočíme: některé z nich se nepoužívají tak často, aby stálo za to jim věnovat samostatné odstavce; na třídu NSBundle se podíváme podrobněji až při popisu Application Kitu, kdy bude místo na podrobné vysvětlení systému, který Cocoa využívá pro lokalizaci (a v němž právě třída NSBundle hraje velmi významnou roli). Ukázky využití třídy NSCharacterSet jsme viděli v minulých příkladech. Dnes se proto podrobněji podíváme na třídu NSConnection (a třídy, jež s ní spolupracují) — budeme se tedy zabývat distribuovanými objekty.

Na rozdíl od minulých příkladů neskočíme rovnýma nohama do popisu konkrétních tříd a ukázek příkladů; vzhledem k tomu, že distribuované objekty jsou dodnes poměrně málo známý prostředek (ačkoli jsou programátorsky nesmírně pohodlné, efektivní, a programátoři je mají k dispozici nejméně od roku 1992), popíšeme si nejprve princip distribuovaných objektů — a až potom si ukážeme konkrétní prostředky a nějaké příklady.

Distribuované objekty

Nejprve bychom si měli vysvětlit o co vlastně vůbec jde. Zhruba řečeno, distribuované objekty umožňují komunikaci mezi objekty, jež nejsou součástí stejného adresového prostoru (a potenciálně ani téhož programu, téhož systému, téhož počítače...). Na rozdíl od klasických služeb pro komunikaci mezi různými procesy — jako jsou např. BSD sockety — distribuované objekty znovu využívají nesmírně silnou vlastnost objektového programování, která se nazývá polymorfismus: jde o to, že s "cizími" objekty komunikujeme přesně stejně, jako s "vlastními".

Podívejme se na naprosto triviální prográmek, který spočte průměrnou hodnotu objektů, jež dostane v poli (předpokládá se přitom, že objekty jsou schopny reagovat na standardní zprávu intValue):

int stupidAverage(NSArray *a) {
  NSEnumerator *en=[a objectEnumerator];
  id o;
  int sum=0;
  while (o=[en nextObject]) sum+=[o intValue];
  return sum/[a count];
}

Představte si, že máte již dávno tuto službu hotovu, a nyní z nějakého důvodu je zapotřebí spočíst průměrnou hodnotu pole, jež není součástí aplikace, ale je uloženo na serveru. V klasickém prostředí bychom měli jen dvě možnosti:

S distribuovanými objekty naopak není co řešit: jestliže již dávno hotové a odladěné rutině stupidAverage předáme jako argument pole, které leží na serveru, bude vše fungovat zcela korektně: NSEnumerator bez obtíží zpřístupní jednotlivé prvky pole (jež samy dokonce mohou třeba každý ležet na jiném serveru, aniž by to přineslo jakékoli komplikace!), a zpráva intValue korektně zjistí jejich číselné hodnoty.

Principy a řešení

V tomto odstavci si vysvětlíme jak vůbec může něco jako distribuované objekty fungovat, a ukážeme si také prostředky, jež nám Cocoa (a Objective C nebo jiný objektový jazyk) pro dosažení takového programátorského pohodlí nabízí. Nejprve si připomeneme několik základních pojmů ohledně objektového systému:

Je asi na první pohled víceméně zřejmé, že je-li tomu tak, není problém "balíček" representující zprávu zakódovat, výsledek odeslat — pomocí standardních služeb TCP/IP — na jiný počítač; tam pak se zpráva opět dekóduje a předá cílovému objektu. Vyřešíme-li několik technických problémů (především s identifikací objektů a s navazováním spojení), můžeme tento mechanismus implementovat — a máme funkční distribuované objekty.

Zástupné objekty (proxy)

V předcházejícím textu jsme abstrahovali od řady detailů, které naši krásně jednoduchou představu o DO malinko komplikují. Prvnímu z nich věnujeme tento odstavec; jedná se o problém s identifikací objektu: je-li k tomu využita adresa — a tak tomu, jak víme, skutečně je — nemáme žádnou možnost identifikovat objekt, který neleží v "našem" adresovém prostoru.

Nejběžnějším řešením je využití zástupného objektu (v anglické terminologii "proxy"). Chceme-li komunikovat s objektem, který leží v jiném adresovém prostoru, vytvoříme si v našem adresovém prostoru zástupný objekt — jednoduchý objekt, který nedokáže nic jiného, než vzít kteroukoli zprávu kterou dostane, zakódovat ji, doplnit cílovou adresou a odeslat ji po síti cílovému procesu (který ji dekóduje a předá objektu na cílové adrese ve svém adresovém prostoru). Na obrázku je zástupný objekt označen P podle anglické terminologie (proxy):

DO_Obr1

Zastavme se na chvilku: schopnost zakódování zprávy a odeslání po síti by jistě mohla být naprogramována přímo jako součást objektu O1; proč tedy komplikovat věci a vytvářet nějaké zástupné objekty? Kdo četl pozorně, již odpověď zná — právě kvůli transparentnosti služeb DO. Objekt O1 je docela obyčejný standardní objekt, jehož programátor vůbec nemusel s nějakou komunikací po síti počítat; z hlediska objektu O1 probíhá naprosto standardní komunikace mezi ním a jeho partnerem (který má vlastnosti objektu O2 a adresu objektu P). Za chvíli se může situace změnit — uživatel např. uzavře dokument ležící na jiném počítači a otevře jiný, lokální dokument — a objekt O1 bude přesně stejně, bez jakékoli změny, komunikovat s lokálním partnerem O3:

DO_Obr2

Na tomto místě je také vhodné si uvědomit, že a proč plnohodnotný systém distribuovaných objektů nutně vyžaduje objektový programovací jazyk (typu Objective C): jde o to, že aby mohl objekt O1 bez obtíží stejným způsobem komunikovat přímo s objektem O3 jako se zástupným objektem P, musíme mít k dispozici polymorfismus: programovací jazyk musí umožňovat zaslání libovolné zprávy libovolnému objektu, s tím, že až za běhu se zprávě připřadí odpovídající implementace. Jazyky typu C++, jež polymorfismus nemají (přesně řečeno, mají jej jen ve velmi omezené míře, pro objekty, jež mají společnou nadtřídu), to zajistit nemohou: abychom např. v C++ mohli takový program vůbec napsat, musel by být zástupný objekt P objektem téže třídy, jako objekty O2 a O3 — a to je na první pohled zřejmý nesmysl.

ORB

Vraťme se však k distribuovaným objektům: na straně příjemce se "někdo" musí postarat o dekódování přijaté zprávy a o její předání cílovému objektu. Na straně odesilatele by se sice v principu o vše mohly postarat zástupné objekty, ale komunikace je jen málokdy jednosměrná — objekt, který odeslal zprávu, chce obvykle dostat zpět výsledek jejího zpracování; často se sám cílový objekt obrací na odesilatele původní zprávy s vlastními zprávami, požadujícími další data a údaje...

Na obou stranách je proto zapotřebí programový modul, který fakticky zajišťuje všechny potřebné úlohy:

Pro tento programový modul se vžilo označení Object Request Broker — ORB. V prostředí Cocoa je reprezentován objektem třídy NSConnection (v ilustraci označeným "Conn"); celkovou situaci si můžeme prohlédnout na dalším obrázku — postupné kroky, ve kterých probíhá předání zprávy, jsou zde očíslovány:

DO_Obr3

  1. objekt O1 odesílá standardním způsobem zprávu objektu, s nímž komunikuje (a sám nemusí vůbec tušit, že se jedná o zástupný objekt);
  2. zástupný objekt zprávu doplní adresou cílového objektu v cílovém adresovém prostoru a celek předá ORBu;
  3. ORB zprávu i cílovou adresu zakóduje a odešle (nejspíše protokolem TCP/IP) ORBu v cílovém procesu;
  4. ORB v cílovém procesu zprávu dekóduje a předá ji objektu O2 na cílové adrese.

Pokud by zpráva vracela nějaké výsledky, probíhalo by jejich předání přesně opačným způsobem: objekt O2 by je zcela standardně předal ORBu (přičemž by vůbec nemusel vědět, že nekomunikuje přímo s objektem O1), ten by je zakódoval a odeslal po síti, ORB v procesu 1 by je dekódoval a prostřednictvím zástupného objektu předal zpět objektu O1.

ORB obvykle zajišťuje řadu dalších úkolů — udržuje např. statistiku přijatých a odeslaných zpráv, stará se o kódování a dekódování obecných dat, která mohou být součástí zprávy, a udržuje si přehled o zástupných objektech (v příštím odstavci uvidíme proč). Úkolem ORBu je také řešit problémy, které nastanou jestliže se spojení přeruší, předávat případné výjimky (hlášení o chybových stavech) a podobně.

Vyhledání partnerů

Jak jsme se zmínili v minulém odstavci — a jak si pozorný čtenář jistě už sám dávno uvědomil — musí vůbec prvním krokem v distribuovaném systému být vyhledání objektu, se kterým chceme komunikovat. Vazba mezi objekty uvnitř jednoho adresového prostoru může (ale samozřejmě nemusí) být vytvořena staticky již při programování objektové aplikace; vazba mezi objekty uloženými v různých adresových prostorech ale musí být navázána dynamicky: je nutné nějak vyhledat cílový objekt, zjistit jeho adresu, identifikaci procesu ve kterém leží a identifikaci počítače, na kterém tento proces běží, a na základě těchto údajů vytvořit jeho zástupný objekt.

Poměrně jednoduchá je situace v případě, že již existuje spojení mezi některými dvěma objekty, a je zapotřebí navázat další spojení mezi jinou dvojicí objektů. V takovém případě prostě jeden objekt pošle druhému zprávu, obsahující odkaz na 'nový' objekt (tj. jeho adresu — uvědomme si znovu, že odesílající objekt vůbec neví, že komunikace není lokální). ORB v cílovém procesu musí takový odkaz zpracovat speciálním způsobem — namísto toho, aby jej přímo předal cílovému objektu, vytvoří zástupný objekt, reprezentující předávaný objekt; cílovému objektu předá adresu zástupného objektu.

DO_Obr4

Obrázek ukazuje konkrétní příklad: objekt O1 odeslal (skrze proxy) objektu O2 zprávu, jejímž parametrem je objekt O3 (ve skutečnosti tedy jeho adresa — např. 0x12E0004). Zástupný objekt tuto zprávu předá ORBu, ten ji standardně zakóduje a předá ORBu v cílovém procesu. Ten zprávu dekóduje a přitom zjistí, že její součástí je odkaz na objekt. Vytvoří tedy zástupný objekt P3, a uloží do něj identifikaci procesu 1 (a počítače, na kterém proces 1 běží), a adresu 0x12E0004. Pak vezme adresu nově vytvořeného zástupného objektu — dejme tomu 0x341F01 — a předá ji objektu O2 na místě původního parametru zprávy.

Kdykoli potom bude chtít objekt O2 odeslat zprávu "objektu O3", odešle ji ve skutečnosti zástupnému objektu P3. To je ale v naprostém pořádku, protože zástupný objekt P3 jakoukoli zprávu kterou dostane předá již známým mechanismem na správnou adresu — objektu O3 v procesu 1.

ORB přitom musí mít přehled o již vytvořených zástupných objektech. Je totiž snadno možné, že zanedlouho znovu některý objekt odešle z procesu 1 do procesu 2 zprávu, jejímž parametrem bude objekt O3. Nebylo by praktické ani efektivní pokaždé vytvářet nový zástupný objekt; namísto toho ORB musí poznat, že zástupný objekt pro "objekt v procesu 1 s adresou 0x12E0004" již existuje, a že se jedná o P3; nebude proto vytvářet nový zástupný objekt, ale pouze nahradí původní adresu ve zprávě adresou zástupného objektu P3.

Jak vidíme, systém distribuovaných objektů není zase až tak úplně jednoduchý (a to jsme zatím zdaleka nevyřešili všechny problémy; některým z nich — jako je např. řešení problémů při přerušeném spojení — se v tomto článku ani věnovat nebudeme). Důležité ale je, že celá složitost leží na samostatném programovém modulu (v našem případě na implementaci tříd NSConnection a NSProxy), a vůbec nijak se nepromítá do implementace objektů, které služeb DO využívají — pro ty je stále celý systém naprosto transparentní a komunikují spolu přesně stejně, jako kdyby všechny ležely v jediném adresovém prostoru. Jinými slovy, z hlediska objektů O1, O2 a O3 — a především z hlediska jejich programátora — situace vypadá prostě a jednoduše takto:

DO_Obr5

Dosud jsme však nevyřešili navázání vůbec prvního spojení mezi oběma procesy. Zde je nutné využít služeb nějakého globálního systému (globálního z hlediska počítačové sítě), který umožní objektům v různých procesech se navzájem vyhledat. To je prakticky jediné místo, ve kterém se distribuovaný systém z hlediska programátora liší od monolitického: zatímco v monolitickém jsou záklůadní vazby mezi objekty vytvořeny při překladu staticky, v distribuovaném je třeba pro jejich vytvoření použít služeb třídy NSConnection (za chvilku si ukážeme příklad, ve kterém uvidíme jak se to dělá).

Předávání dat

Součástí zpráv předávaných po síti mohou být i obecná data, uložená jako jejich parametry (připomeňme, že součástí "balíčku", representujícího zprávu, je obojí — identifikace zprávy i její argumenty). ORB na straně odesilatele musí proto data zakódovat do formátu, vhodného k přenosu; ORB na straně příjemce musí data opět dekódovat. Je třeba si uvědomit, že se nejedná o triviální úlohu. Jen pro rámcovou ilustraci problémů se podívejme na řešení pro některé základní datové typy:

Celočíselné hodnoty se kódují prostě jako řada bytů, tolik bytů kolik je zapotřebí na odpovídající číslo (tj. jeden byte pro typ char jazyka C, čtyři byty pro int apod.). Důležité však je pořadí bytů — není nikde zaručeno, že vysílající i přijímající procesy běží na počítačích se stejnou architekturou, takže jeden z nich může pracovat v little-endian módu a druhý v big-endian módu.

Adresy objektů přímo předávat nelze — jak jsme si ukázali, musí ORB na straně příjemce vyhledat nebo vytvořit zástupný objekt a předat jej namísto původního objektu. Systém distribuovaných objektů, který je součástí API Cocoa, navíc umožňuje kromě odkazu na objekt předávat objekt jako celek ("hodnotou"); na tuto možnost se podíváme v příštím odstavci (jde o velmi luxusní službu, např. známý systém distribuovaných objektů CORBA ji nemá).

Největší problém je s ukazateli (a tedy také s poli, která jsou předávána jako ukazatele). Ukazatel není nic jiného než adresa; je tedy zřejmé, že předat jej přímo do jiného adresového prostoru nemá smysl. Triviálním řešením by bylo ukazatele prostě zakázat; to by ale nepříjemně omezilo polymorfismus a transparentnost DO (protože objekt, který má využívat komunikaci, by pak musel být psán speciálním způsobem — nesměl by využívat ukazatelů). Podívejme se na způsob jakým tento problém řeší Cocoa:

  1. ORB na straně odesilatele zjistí velikost dat, na které ukazatel míří;
  2. ORB na straně příjemce alokuje ve svém adresovém prostoru odpovídající blok;
  3. kompletní data se přenesou k příjemci a tam se uloží do alokovaného bloku;
  4. teprve nyní předá ORB na straně příjemce zprávu cílovému objektu; adresu v ukazateli přitom nahradí adresou nově alokovaného bloku;
  5. když je objekt se zprávou hotov, přenesou se data z bloku zpět do adresového prostoru původního odesilatele a uloží se na místo původních dat (takže případné změny, které v datech cílový objekt provedl, nebudou ztraceny);
  6. teprve nyní je pomocný blok na straně příjemce uvolněn.

V Objective C máme navíc možnost tento mechanismus omezit explicitním určením je-li blok dat pouze vstupní (v tom případě se vynechá bod 5) nebo pouze výstupní (pak se vynechá bod 3) pomocí modifikátorů in a out:

-(void)vstup:(int in*)vstup vystup:(int out*)vystup oba:(int*)oba; // nebo (int inout*)oba

Určili jsme, že argument vstup je pouze vstupní (takže lze pro něj vynechat bod 5) a že argument vystup je pouze výstupní (takže lze pro něj vynechat bod 3). Argument oba je vstupní i výstupní, takže se provedou všechny body.

Předávání objektů

Standardní mechanismus předávání objektů již známe — cílový ORB vytvoří nebo vyhledá zástupný objekt pro předávaný objekt, a cílovému objektu předá namísto původního parametru adresu nového zástupného objektu. Představme si ale situaci, ve které je "předaný objekt" (tj. ve skutečnosti zástupný objekt) velmi intenzivně a často využíván; pak v tomto modelu dojde ke značnému zatížení sítě a také k nepříjemnému zpomalení aplikace (protože síť je obvykle poměrně pomalá).

Špičkové systémy distribuovaných objektů proto umožňují v takovýchto případech namísto zástupného objektu předat skutečný objekt — přesněji řečeno, ORB v cílovém procesu vytvoří přesnou kopii předávaného objektu, a cílovému objektu předá adresu této kopie. Připomeňme si příklad s vytvořením zástupného objektu P3 pro objekt O3; pokud bychom využili předání objektu, vypadala by situace tak, že objekt O3 v procesu 2 je přesnou kopií objektu O3 v procesu 1:

DO_Obr6

V Objective C zajišťuje tuto službu modifikátor bycopy, použitý v definici objektového argumentu:

-(void)foo:(bycopy id)obj1 bar:(id)obj2;

První argument této zprávy — obj1 — bude předáván "hodnotou", tj. vytvoří se jeho kopie. Druhý argument obj2 bude předán normálně (tj. vytvoří se pro něj zástupný objekt).

Možnost předávání objektů namísto zástupců je v některých případech velmi šikovná a může být obtížné se bez ní obejít; na druhou stranu si musíme uvědomit, že klade nemalé nároky na objektový systém, nad kterým DO pracují. Aniž bychom zacházeli do podrobností, ukážeme si nejzásadnější problémy které je třeba vyřešit a jako příklad velmi stručně nastíníme řešení, které nabízí Cocoa:

Konkrétní třídy

Máme-li konečně jasno v tom, co to vlastně systém distribuovaných objektů je a jak funguje, můžeme se podrobněji podívat na konkrétní třídy API Cocoa, jež jeho služby zajišťují.

NSConnection

Objekty třídy NSConnection reprezentují ORB — každé spojení mezi dvěma procesy je tedy zajišťováno dvojicí objektů NSConnection, na každé straně spojení jedním. Z hlediska navazování spojení je vždy jeden proces "serverem":

Libovolný počet "klientů" si pak může vyžádat vytvoření vlastního objektu NSConnection pro spojení se "serverem" se zadaným jménem; pokud takové jméno existuje, vytvoří se na straně klienta automaticky objekt NSConnection, naváže se spojení mezi ním a serverem, a vytvoří se proxy, reprezentující u klienta "hlavní objekt" serveru. Všechny ostatní zástupné objekty jsou pak vytvářeny podle potřeby automatickým mechanismem, popsaným výše.

Je důležité si uvědomit, že rozlišení klient/server je podstatné pouze z hlediska navazování spojení: zde je serverem jednoznačně ten, kdo své jméno registruje, klientem je pak ten, kdo si vyžádá spojení se serverem na základě daného jména. Konkrétní komunikace mezi oběma procesy pak již není nijak omezena, a závisí jen na tom, jak celý systém navrhneme. Nejsme tedy nikterak omezeni na systémy klient/server; spíše jde o plnohodnotnou komunikaci typu peer-to-peer.

Typický kód serveru pro určení hlavního objektu a registraci jména vypadá ve FoundationKitu takto:

// vytvoříme hlavní objekt
id root=[MujHlavniObjekt new];
// každý thread má svůj standardní connection
id myconn=[NSConnection defaultConnection];
// zaregistrujeme hlavní objekt
[myconn setRootObject:root];
// zaregistrujeme jméno
if (![myconn registerName:@"Jméno serveru..."]) {
  // nelze registrovat ... buď již je toto
  // jméno zaregistrováno, nebo nastala
  // nějaká chyba v operačním systému
}

Kód klienta je ještě mnohem jednodušší — jediným příkazem přímo získáme zástupný objekt, který representuje hlavní objekt serveru se zadaným jménem; odpovídající objekt NSConnection se vytvoří automaticky a programátor se jím nemusí vůbec zabývat:

id server=[NSConnection
  rootProxyForConnectionWithRegisteredName:@"Jméno serveru..."
  host:@"*"];
if (server==nil) {
  // server se zadaným jménem není k dispozici
}

Jestliže nyní klient pošle jakoukoli zprávu "serveru" — například

[server haloJsiTam];

dostane tuto zprávu postupem, který jsme popsali v minulém odstavci, objekt root v serveru.

Třída NSConnection nabízí řadu dalších služeb, které usnadňují řízení komunikace mezi oběma procesy. Pomocí metody allConnections např. můžeme získat seznam všech objektů NSConnection, které jsou v tomto procesu (přesněji threadu) otevřeny — tj. seznam všech aktivních spojení do jiných procesů či threadů. Metody setReplyTimeout: a setRequestTimeout: určují, jak dlouho má ORB čekat na reakci druhého procesu, než prostřednictvím notifikací (nesmírně flexibilního mechanismu pro předávání informací, se kterým se seznámíme příště) informuje každého koho to zajímá o tom, že spojení bylo přerušeno. Řada dalších zpráv umožňuje řídit spojení, zjišťovat statistiky ohledně nevázaného spojení, dynamicky rozhodovat zda se konkrétní spojení smí navázat nebo ne, a tak dále.

NSProxy a NSDistantObject

Objekty třídy NSProxy reprezentují zástupné objekty; z programátorského hlediska tedy na nich vlastně dohromady není co popisovat, protože proxy se vždy polymorfně chová jako objekt, na který odkazuje.

Přesto existuje jedna specifická zpráva, kterou interpretuje právě proxy (přesněji řečeno, jeho podtřída NSDistantObject — což je z hlediska tohoto orientačního popisu lhostejné). Musíme si uvědomit, že odesílání zprávy je ve skutečnosti ještě o něco složitější, než jak jsme je dosud popsali: uvedli jsme, že systém distribuovaných objektů zabalí nejprve zprávu a její parametry do balíčku, a ten předá cílovému procesu. Aby však bylo vůbec možné zprávu a její parametry zabalit, musíme znát nejprve typy parametrů, které skutečný objekt ve zprávě očekává (připomeňme rozbor komplikovaného předávání parametrů různých typů), a na ty se musí nejprve zeptat procesu, ve kterém skutečný objekt — a tedy i informace o něm — leží. Při odeslání jednoduché zprávy tedy po síti ve skutečnosti musí proběhnout (nejméně) čtyři pakety:

  1. dotaz na typy parametrů dané zprávy;
  2. odpověď, obsahující popis parametrů a jejich typů;
  3. vlastní zpráva (zakódovaná na základě popisu z minulého bodu);
  4. výsledek zprávy (tento krok lze vynechat u zpráv typu oneway, viz níže).

Pro zvýšení efektivity můžeme ušetřit první dva kroky jednoduchým způsobem: informujeme zástupný objekt jednorázově o typech parametrů všech zpráv, které je cílový objekt schopen zpracovat (samozřejmě to není možné v případech, kdy cílový objekt zprávy dynamicky přesměrovává; to je ale dost vyjímečný případ). Objective C nabízí prostředek pro zápis seznamu zpráv a typů jejich parametrů; ten se jmenuje protokol. Každému proxy pak můžeme zprávou setProtocolForProxy: — chceme-li — přidělit protokol, který popisuje zprávy, zpracovávané objektem, který proxy reprezentuje. Tím si uspoříme odesílání prvních dvou paketů: není třeba se na atributy zprávy ptát, protože jsou známy díky protokolu.

Jazykové prostředky Objective C

Díky flexibilitě objektového systému založeného na Objective C není zapotřebí rozšiřovat programátorský model při práci s distribuovanými objekty téměř o nic (pro srovnání stojí za to se podívat např. na systém distribuovaných objektů CORBA, který se neobejde bez vlastního, a velmi komplikovaného, API).

V ObjC stačí přidat jen pět modifikátorů. S většinou z nich jsme se již seznámili v odstavcích o předávání dat a objektů; jediný zbývající modifikátor je oneway, který se používá takto:

-(oneway void)foobar;

Určili jsme, že po odeslání zprávy foobar není třeba čekat na její ukončení. Kód, který zprávu odeslal, tedy ihned pokračuje dále, paralalně s odesíláním zprávy do jiného procesu či threadu a s jejím zpracováním.

Příklad

S příkladem na použití distribuovaných objektů jsme se již setkali — byl jím kód serveru a klienta v sedmém příkladu pro Objective C.

Shrnutí

Ukázali jsme si práci s distribuovanými objekty a základní služby, jež ve Foundation Kitu nabízejí odpovídající třídy. V příštím dílu se budeme konkrétním třídám Foundation Kitu věnovat již naposledy.


Zpět Obsah Další

Copyright © Chip, O. Čada 2000-2003