Nastal čas na kakao - Třídy Foundation Kitu - MujMAC.cz - Apple, Mac OS X, Apple iPod

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Seriály

Více seriálů



Začínáme s

Nastal čas na kakao - Třídy Foundation Kitu

4. února 2005, 00.00 | V minulém dílu našeho seriálu jsme dokončili praktický příklad programování v Cocoa tím, že jsme si ukázali ty nejjednodušší základy ladění. Mohu-li navázat na diskusi k článku: samozřejmě, že k ladění – stejně, jako k praktickým příkladům – se ještě vrátíme podrobněji; aby to však mělo rozumný smysl, je zapotřebí, abychom měli lepší představu o tom, jaké služby vlastně nabízejí knihovny standardních tříd Cocoa.

V minulém dílu našeho seriálu jsme dokončili praktický příklad programování v Cocoa tím, že jsme si ukázali ty nejjednodušší základy ladění. Mohu-li navázat na diskusi k článku: samozřejmě, že k ladění – stejně, jako k praktickým příkladům – se ještě vrátíme podrobněji; aby to však mělo rozumný smysl, je zapotřebí, abychom měli lepší představu o tom, jaké služby vlastně nabízejí knihovny standardních tříd Cocoa. Dnes a v následujících několika pokračováních se proto blíže podíváme na to, co nám umožňuje základní framework Foundation Kit.

Přehled tříd

V dnešním dílu si jen tak projdeme většinu tříd, jež ve Foundation Kitu jsou, a o každé si stručně řekneme k čemu slouží. Nebojte se, API Cocoa je navrženo velmi rozumně, takže tříd nebude nijak mnoho – na rozdíl od stovek tříd, jež obvykle bývají v prostředích založených na C++, stačí pro kompletní API Foundation Kitu méně než padesát tříd, a to je mezi nimi (vzhledem k paradigmatu proměnných a neproměnných objektů) ještě řada dvojic, jež z hlediska nabízených služeb representují vlastně de facto jediný typ objektu, jehož možnosti jsou do dvou tříd rozděleny jen z technických důvodů.

Zde je skoro kompletní seznam veřejných tříd Foundation Kitu (skryté podtřídy si samozřejmě popisovat nebudeme, proto jsou skryté, aby to nebylo nikterak zapotřebí) se stručným popisem jejich funkce:

NSObject je základní třídou celé objektové hierarchie Cocoa – jsou od něj odvozeny nejen téměř všechny ostatní třídy Foundation Kitu, ale i třídy všech ostatních kitů, a až na naprosté výjimky i všechny třídy, jež vytvářejí programátoři ve vlastních projektech.

Jedinou výjimkou je malá skupina velmi speciálních tříd, jež representují skutečně zvláštní objekty – např. třída NSProxy, jež representuje objekt ležící v jiném adresovém prostoru, není dědicem NSObjectu. Standardní metody třídy NSObject jsou tedy k dispozici pro jakýkoli objekt, se kterým se v systému Cocoa potkáme (tyto služby ostatně implementuje i NSProxy). Především jde o základní služby zajišťující korektní práci v objektovém prostředí: možnost zjistit dynamicky třídu daného objektu, možnost ověřit zda objekt dokáže nebo nedokáže zpracovat zprávu se zadaným jménem, odeslání zprávy zadaného jména objektu, zjištění zda objekt odpovídá nebo neodpovídá zadanému protokolu...

Navíc je zde několik obecných služeb, jež se pro pohodlí programátora vyplatí nabízet přímo na úrovni třídy NSObject, podpora pro persistenci objektů, a v neposlední řadě služby garbage collectoru (metody retain, release a autorelease). Na tom s nimi spolupracují také objekty třídy NSAutoreleasePool, které navíc umožňují programátorovi chování garbage collectoru v potřebné míře řídit.

NSArray a NSMutableArray jsou třídy, zajišťující práci s obecnými poli jiných objektů. K objektům v nich uložených můžeme přistupovat na základě indexů nebo přímo na základě adresy objektu (můžeme např. zjistit index daného objektu v poli, či vyřadit konkrétní objekt z NSMutableArray bez ohledu na jeho index); pole můžeme také setřídit nebo procházet sekvenčně s využitím instance třídy NSEnumerator.

Podstatným rysem tříd NS(Mutable)Array – stejně jako všech ostatních kontejnerů Cocoa – je to, že se jedná o beztypové kontejnery: můžeme do nich bez nejmenších obtíží ukládat libovolné objekty. Zásadně se tedy liší od omezených kontejnerů např. C++, kde je třída objektů, ukládaných do kontejneru, pevně a neměně dána. Není např. problém mít v jediném poli postupně NSString, NSNumber, NSData, a vnořené pole NSArray, obsahující opět další libovolné objekty: to umožňuje vytváření nejobecnějších datových struktur, aniž bychom kvůli tomu museli definovat nové třídy.

Významné je také to, že – v naprosté shodě s jazyky Objective C nebo Java, jež pro objekty užívají zásadně referencí – i kontejnery Cocoa pracují s referencemi (a ne s hodnotami). Výhody jsou zásadní: objekty mohou být mezi různými kontejnery bez nejmenších problémů sdíleny, kontejnery mohou representovat zcela obecné objektové sítě (i nehierarchické, a speciálně kontejner může – jakkoli to v praxi nebývá obvykle zapotřebí – klidně obsahovat i sám sebe).

• Třídy NSSet, NSMutableSet a NSCountedSet reprezentují množiny jiných objektů, na rozdíl od tříd NS(Mutable)Array v nich nejsou objekty seřazeny. Zato však nabízejí mnohem efektivnější testování je-li objekt součástí množiny nebo ne – doba potřebná pro zjištění je-li objekt uložen v NS(Mutable)Array je závislá na počtu objektů v poli uložených, zatímco množina je to schopna zjistit v konstantním čase. Je tedy velmi efektivní využívat množinu kdykoli, kdy potřebujeme zajistit jednoznačnost objektů – samozřejmě opět objektů zcela libovolné třídy (jež korektně podporuje hashování).

NSCountedSet reprezentuje speciální množinu, ve které může jeden a tentýž objekt být uložen vícekrát (přesně řečeno, v kontejneru je samozřejmě jen jeden odkaz na každý objekt, vedle něj však kontejner udržuje čítač referencí). S využitím této třídy je např. frekvenční analýza textu vlastně hotova: jen uložíme všechna slova do kontejneru, a pak si vyžádáme výpis jeho obsahu včetně čítačů.

Stejně jako tomu je u pole, můžeme všechny objekty množiny sekvenčně projít pomocí třídy NSEnumerator. To ostatně platí pro všechny kontejnery v Cocoa: není mezi nimi žádný, který by nedokázal nabídnout objekt NSEnumerator pro sekvenční procházení.

• Již zmíněné třídy NSDictionary a NSMutableDictionary reprezentují hashovací tabulky, které umožňují ukládání dvojic libovolných jiných objektů <klíč, data> a zajišťují vyhledání datového objektu na základě klíče v čase nezávislém na počtu dvojic v tabulce (stejně jako množiny, i tyto třídy využívají hashování).

Tabulka ukládající dvojice objektů a nabízející velmi rychlé vyhledání je nesmírně praktickým prostředkem; proto jsou objekty tříd NS(Mutable)Dictionary spolu s poli NSArray a s NSStringy tak často využívány: uvědomme si, jak často se v programech využívá nejrůznějších variant pojmenováváni a identifikace objektů.

• Objekty tříd NSString a NSMutableString odpovídají textovým řetězcům a nabízejí opravdu neobvykle bohatou sadu služeb, od převodů čísel do a z textového tvaru, přes ekvivalent klasické funkce printf až po metody, interpretující řetězec jako jméno souboru nebo adresáře a provádějící potřebné operace nad systémem souborů. Samozřejmostí jsou i takové služby jako vyhledávání řádků a odstavců v textových řetězcích, jež obsahují delší text.

Objekty tříd NS(Mutable)String jsou navíc schopny pracovat s texty v téměř libovolném kódování včetně UNICODE, takže podporují znaky prakticky všech ve světě běžně používaných abeced. Chceme-li např. načíst text v kódové stránce Windows CP1250 a uložit jej v ISO Latin2, nebo dokonce načíst text v UTF8 a uložit jej v kódování Shift JIS, není třeba prakticky vůbec programovat: stačí zavolat odpovídající službu třídy NSString.

• Ačkoli pro práci s čísly v Objective C většinou využíváme standardní "céčkové" typy (int, long,...), jsou k dispozici i třídy, jež všechny standardní typy jazyka C mohou representovat pomocí objektů: NSValue a NSNumber. První z nich slouží jako objektová abstrakce hodnot všech typů známých z jazyka C a Objective C; druhá (která je jejím dědicem) se omezuje na číselné typy a nabízí pro ně specifické a pohodlnější API. Navíc je k dispozici třída NSDecimalNumber, jejíž objekty representují dekadická čísla s extrémně vysokou přesností (až do 38 platných míst), a nabízejí širokou paletu služeb, od aritmetiky až po velmi obecné zaokrouhlování.

NSData a NSMutableData jsou abstrakcí beztypových dat. Jejich přímým ekvivalentem v neobjektovém programování je blok paměti, objektový přístup prostřednictvím tříd NS(Mutable)Data však nabízí daleko širší paletu služeb. Velmi často navíc tyto objekty zajistí i nesrovnatelně vyšší efektivitu díky spolupráci se stránkovacím mechanismem: např. instance třídy NSData, vytvořená načtením obsahu souboru, totiž může být – a pro větší soubory také obvykle bývá – realizována jako pouhý odkaz na data v souboru, zajištěný prostřednictvím systému virtuální paměti.

Všechny třídy popsané výše – od NSArray až po NSData – mají trochu výsadní postavení: representují totiž obecné datové typy, a s využitím kontejnerových tříd umožňují sestavit prakticky libovolnou datovou strukturu, aniž by bylo zapotřebí definovat nové třídy. My si samozřejmě později ukážeme využití této možnosti na několika příkladech.

NSBundle nabízí aplikaci velmi pohodlný přístup ke zdrojům v nejobecnějším smyslu slova – od doplňkových obrázků, zvuků nebo jakýchkoli podobných dokumentů "zabalených" do aplikace až po dynamicky zaváděné třídy z doplňkových modulů. Třída přitom automaticky zajišťuje lokalizaci (tj. výběr zdroje na základě jazyka, pro který je zdroj určen, a momentálně platných uživatelských jazykových předvoleb).

• Objekty tříd NSCharacterSet a NSMutableCharacterSet reprezentují libovolnou množinu znaků – slouží tedy k podobným účelům, pro které se v PASCALu využívaly proměnné typu 'set of char'. Na rozdíl od nich však podporují kompletní znakovou sadu UNICODE a nabízejí daleko bohatší sadu operací. Nejčastěji se využívají spolu se třídou NSScanner, o které se zmíníme níže; dokáží s nimi však spolupracovat i ostatní třídy: např. objekt třídy NSString dokáže na požádání vyhledat první výskyt znaku ze zadané množiny.

NSCoder reprezentuje univerzální služby, potřebné pro zakódování jakéhokoli objektu do podoby potřebné např. pro jeho uložení do souboru nebo odeslání po síti na jiný počítač, a jeho opětovné dekódování. Jeho podtřídy NS(Keyed)Archiver a NS(Keyed)Unarchiver pak zajišťují konkrétní archivaci a dearchivaci objektů programátorsky velmi pohodlným způsobem.

Uvědomíme-li si, že objekty mohou vytvářet velmi obecné objektové sítě, kdy jeden objekt může být sdílen více objekty jinými, nebo kdy dokonce může kontejner obsahovat v extrémním případě i sám sebe, je zřejmé, že korektní archivace takovýchto objektových sítí nebude nikterak triviální. Služby tříd NSArchiver a NSUnarchiver všechny tyto problémy korektně řeší, takže jejich pomocí můžeme zajistit persistenci sebesložitějších objektových sítí.

Stojí za to také zdůraznit, že explicitně programovat persistenci objektů s využitím těchto tříd musíme jen pro třídy, jež sami vytváříme. Nejenže všechny standardní třídy Cocoa persistenci samy automaticky podporují; základní datové třídy a kontejnery popsané hned zpočátku (od NSArray až po NSData) navíc nabízejí další služby pro zápis do a načtení ze souborů ve standardním a obecně čitelném formátu XML – díky tomu je snadné takové objektové sítě zpracovávat (nebo vytvářet) i externě.

• Třídy NSConditionLock, NSLock a NSRecursiveLock nabízejí velmi pohodlná a flexibilní primitiva pro synchronizaci paralelně běžících procesů nebo threadů. Vytváření a řízení procesů a threadů Foundation Kit podporuje prostřednictvím tříd NSTask a NSThread.

• Třída NSConnection spolu s pomocnými třídami NSProxy a NSDistantObject je základem technologie distribuovaných objektů – zajišťuje navázání a udržování spojení a předávání zpráv mezi objekty uloženými v různých adresových prostorech nebo dokonce na různých počítačích.

Velmi zhruba řečeno, systém distribuovaných objektů zajišťuje to, že můžeme s objekty z jiného adresového prostoru (tj. obecně z jiné aplikace, běžící klidně na úplně jiném počítači třeba na opačném konci světa) pracovat do posledního detailu přesně stejně, jako kdyby se jednalo o objekty naprosto obyčejné. Díky tomu je psaní distribuovaných aplikací – ať již typu klient-server, nebo peer-to-peer – ve srovnání s prostředími nabízejícími služby jen na úrovni např. socketů v API Cocoa doslova hrou.

• Třídy NSDate a NSCalendarDate reprezentují datum a čas a nabízejí všechny potřebné služby, včetně převodů mezi jednotlivými formáty a vytváření nebo načtení textové podoby data a času. Podobně jako u ostatních služeb Cocoa, jde propracovanost těchto tříd až do extrémů – pokud to např. programátor dané aplikace nezakáže, rozumějí rutiny pro načtení data z textové podoby dokonce i výrazům typu "today" nebo "the day before yesterday"...

• Třída NSException zajišťuje centralizovanou obsluhu výjimek – její služby můžeme zhruba přirovnat např. k mechanismu výjimek v jazyce Java. Všechny třídy Cocoa samozřejmě samy využívají služeb třídy NSException pro hlášení vlastních chybových stavů, a operační systém naopak výjimky, neodchycené aplikačním kódem, loguje a korektně zobrazuje.

Jestliže díky systému distribuovaných objektů mohou všechny třídy Cocoa pracovat v distribuovaném prostředí stejně dobře, jako v monolitické aplikaci, platí to samozřejmě i pro třídu NSException. Důsledkem je např. to. že vyvolá-li výjimku kód serveru, může tuto výjimku odchytit a korektně zpracovat kód klienta, který službu serveru volal.

• Prostřednictvím třídy NSInvocation můžeme velmi pohodlně přesměrovávat zprávy jiným objektům. To umožňuje využít systém vkládání objektů tam, kde by dědičnost byla nevhodná, nešikovaná, nebo kde by její služby byly prostě nepostačující (např. chceme-li vytvořit "dědice" třídy, kterou neznáme, tj. nemáme k dispozici její hlavičkový soubor).

Služby třídy NSInvocation jsou však daleko širší – její pomocí můžeme programově zkonstruovat předání libovolné zprávy s libovolnými parametry libovolnému objektu a zpracovat libovolnou návratovou hodnotu. Jedná se tedy vlastně o "metaprostředek", který slouží v případech kdy v době překladu programu není známa metoda kterou chceme odeslat, její parametry nebo typ její návratové hodnoty.

• Třídy NSNotification, NSNotificationCenter a NSNotificationQueue spravují předávání informací o událostech mezi objekty. Kdykoli objekt změní svůj stav nějakým způsobem, který by mohl být významný pro ostatní objekty (např. okno je zavřeno), odešle zprávu o této události (ve formě objektu třídy NSNotification); třída NSNotificationCenter se pak postará o to, aby tuto zprávu dostaly všechny objekty, které si dříve "předplatily" přijímání podobných zpráv.

Nejenže tyto třídy významně usnadňují tvorbu rozsáhlejších systémů (protože výrazně omezují explicitní závislosti mezi jejich jednotlivými moduly, a tím samozřejmě podstatně snižují pravděpodobnost chyb); podstatné je, že stejně jako všechny ostatní standardní třídy Coca i třídy NSNotification... korektně pracují v distribuovaném prostředí. Možnost využívat takto obecný systém předávání zpráv např. v aplikaci klient-server je tak skvělá, že člověk, který to nevyzkoušel, si to neumí představit...

• Třída NSScanner vlastně není ničím jiným, než patřičně "rozbujelou" objektovou nadstavbou staré dobré funkce scanf: její metody procházejí znakově orientovaný vstup a podle zadaných požadavků z něj vybírají jednotlivé části a případně je převádějí do jiných formátů. Ačkoli NSScanner nenabízí tak silné služby jako standardní kombinace lex+yacc (která je samozřejmě v Mac OS X díky jeho unixovému dědictví k dispozici také), je pro naprostou většinu aplikací dostačující, a na rozdíl od lexe a yacca plně podporuje UNICODE.

• Objekty třídy NSTimer jsou časovými čítači, které čekají určenou dobu (nebo do určené chvíle) a pak odešlou předem zadanou zprávu předem danému objektu. Stojí za zmínku, že základní služby tohoto druhu nabízí už samotná třída NSObject, takže je máme pohodlně k dispozici kdykoli a kdekoli; jen pro složitější případy pak potřebujeme NSTimer.

• Každý operační systém spravuje takovou či onakou databázi standardních hodnot; přístup k této databázi zajišťuje obecným a přenositelným způsobem třída NSUserDefaults.

Foundation Kit obsahuje ještě několik málo dalších tříd, k nichž jsme se nezmínili; většinou jde o speciality určené pro zvláštní a výjimečné případy, s nimiž se naprostá většina programátorů jakživ nepotká.

Příště...

... se podíváme na nejčastěji používané třídy blíže, a ukážeme si jejich využití na praktických příkladech: seznámíme se s využitím beztypových kontejnerů pro sestavení obecné datové struktury a ukážeme si i výhody, jež to přináší proti přípravě vlastních specifických tříd.

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Začínáme s  

 

 

 

Nejčtenější články
Nejlépe hodnocené články
Apple kurzy

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: