Druhé Objective C - 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ů



Software

Druhé Objective C

29. listopadu 2007, 09.00 | Jak jsme si slíbili v minulém článku, v popisu novinek Leoparda začneme tím, co je nového v jazyce Objective C. Po velice dlouhé době, kdy – vyjma doplnění výjimek a synchronizace – nebylo nového nic, se toho tentokrát "urodilo" poměrně dost.

Jak jsme si slíbili v minulém článku, v popisu novinek Leoparda začneme tím, co je nového v jazyce Objective C. Po velice dlouhé době, kdy – vyjma doplnění výjimek a synchronizace – nebylo nového nic, se toho tentokrát "urodilo" poměrně dost. Pojďme si nejprve shrnout všechny novinky, jež se v Objective C objevily:

  • podpora kritických sekcí v kódu a direktivy pro práci s výjimkami nejsou samy o sobě žádnou novinkou; přesto se na ně podíváme blíže ze dvou dobrých důvodů: předně, nové možnosti Objective C 2 ovlivňují jejich využití – a kromě toho stojí za to zdůraznit jeden potenciální problém s direktivou @finally, o němž jsme se v předcházejících dílech zapomněli zmínit;
  • podpora garbage collectoru je na jazykové úrovni poměrně jednoduchá (složitý je samozřejmě runtime);
  • možnost povolit přístupová práva k proměnným objektu pouze modulu, v němž je třída definována;
  • možnost specifikovat dodatečné atributy pro metody a třídy pro vylepšení kontroly při překladu a pro lepší zapouzdření;
  • možnost měnit kdykoli instanční proměnné objektu bez ohledu na podtřídy;
  • rozšíření služeb protokolů a kategorií;
  • nový příkaz pro iteraci objektů v cyklu;
  • nová podpora pro deklarace a definice accesorů.

Kromě toho Objective C 2.0 v Leopardu nabízí řadu nových služeb pro dynamické zpracování zpráv; zastaralé a mnohdy nedostatečně flexibilní poseAsClass: nahrazuje novým mechanismem přímé záměny selektorů (tento mechanismus byl sice k dispozici i předtím, ale přístup k němu byl poměrně komplikovaný). To vše ale jsou věci, které se netýkají přímo vlastního jazyka, ale spíše podpory, již mu prostřednictvím služeb standardní třídy NSObject dává Foundation Kit; popíšeme si je proto až později, až od vlastního jazyka přejdeme k přehledu novinek v knihovnách.

Kritické sekce a výjimky

Jazyková podpora, kterou Objective C nabízí pro podporu kritických sekcí a výjimek, není nová; přesto však stojí za to se k ní vrátit, protože v Objective C 2.0 má některé nové vlastnosti (a také, jak jsme se již zmínili, v rámci našeho seriálu jsme na několik podstatných věcí zapomněli).

Kritické sekce

Podpora kritických sekcí je poměrně jednoduchá – my jsme se o ní zmínili v článku předem slibujícím Leopardí novinky, avšak nepopsali jsme si ji tehdy podrobně. Ačkoli tedy zrovna tato služba se od dob Tygra nikterak nezměnila (vizte ovšem poznámku k efektivitě zpracování výjimek na konci příštího odstavce), je nejvyšší čas tak učinit.

Podpora je založena na direktivě @synchronize, jež následující blok označí jako kritickou sekci:

@synchronize (zámek) {
  ... kritická sekce ...
}

Objective C automaticky vygeneruje kód, který zajistí, aby kritickou sekci mohlo zpracovávat pouze jediné vlákno; pokud se k ní dostane zároveň jiné vlákno, je pozastaveno a na vstupu do kritické sekce čeká, dokud předchozí vlákno kritickou sekci neopustí.

Zámkem může být libovolný objekt (ve skutečnosti se zdá, že kód generovaný pro synchronizaci snese jakoukoli obecnou hodnotu a netrvá na tom, aby šlo právě o adresu objektu; není však samozřejmě dobré na to spoléhat, přinejmenším pro kompatibilitu s budoucími verzemi). Jeho funkcí je prostě a jednoduše rozlišit různé kritické sekce; kupříkladu tedy

@synchronize (pole1) { [pole1 addObject:o]; }
@synchronize (pole2) { [pole2 addObject:o]; }

zajistí, že se více vláken zároveň nepokusí zapisovat do žádného z polí (v daném kódu samozřejmě), avšak nikterak nebude bránit jednomu vláknu v zápisu do pole1 a zároveň – vzhledem k odlišnému zámku – jinému vláknu v zápisu do pole2.

Kritická sekce je automaticky chráněna kódem pro zachycení výjimky; nemusíme se tedy obávat, že by výjimka uvnitř kritické sekce ponechala kritickou sekci uzamčenou. Tím se dostáváme ke druhé části:

Výjimky

Jazykové prostředky pro podporu výjimek Objective C 2.0 mění jen v jediné drobnosti, kterou popíšeme níže; významnější novinkou je to, že klasická makra NS_DURING/NS_HANDLER/NS_ENDHANDLER nyní nejenže jsou kompatibilní s výjimkami definovanými na úrovni jazyka; jsou přepsána přímo do direktiv @try/@catch, zhruba takto:

#define NS_DURING		@try {
#define NS_HANDLER		} @catch (NSException *localException) {
#define NS_ENDHANDLER		}
#define NS_VALUERETURN(v,t)	return (v)
#define NS_VOIDRETURN		return

Další novinkou, jež se neprojeví přímo v jazyce, avšak může mít významné důsledky pro programátorský styl, je to, že firma Apple – pouze ovšem pro čtyřiašedesátibitové buildy! – změnila implementaci výjimek; nadále již nejsou založeny na službách setjmp/longjmp, ale využívají mechanismus výjimek C++. To má dva důsledky:

  • výjimky C++ a Objective C jsou nyní navzájem kompatibilní; výjimka, vyvolaná vnořeným kódem C++, může být zachycena direktivou @catch v Objective C, a naopak;
  • změnila se podstatným způsobem efektivita zpracování: průchod do bloku, chráněného direktivou @try, je nyní velmi efektivní; naopak je daleko "dražší" vyvolání výjimky direktivou @throw.

Prvý z důsledků souvisí s jedinou jazykovou změnou, kterou Objective C pro zpracování výjimek přináší: nyní lze v direktivě @catch použít argument "...", který zachytí skutečně cokoli, speciálně tedy neobjektovou výjimku C++ (a je možné ji předat do vyšší úrovně direktivou @throw bez argumentu).

Důsledkem druhého je určitá změna pohledu, jež velmi dobře souvisí s doporučením Apple výjimek užívat skutečně pouze pro výjimečné (chybové) stavy, a nikdy pro běžné zpracování dat: nestojí takřka nic ochranou @try/@catch opatřit téměř libovolný kód; naopak skutečného vyvolání výjimky bychom se měli vyvarovat, není-li to nutné.

Vzhledem k tomu, že – jak jsme si řekli v minulém odstavci – direktiva @synchronize pro vytvoření kritické sekce obsahuje implicitní ochranu @try/@catch, je to důležité i z hlediska kritických sekcí – ty můžeme používat bez obav o snížení efektivity kódu kdekoli, kde je to zapotřebí.

(Samozřejmě nevhodně umístěná kritická sekce stále může snížit efektivitu tím, že vlákna budou na vstup do ní čekat zbytečně; to je ale jiná otázka.)

Pozor na @finally!

V článku, v němž jsme se s jazykovou podporou pro práci s výjimkami seznámili, jsme uvedli (v podstatě, a k tomu s překlepem v názvu direktivy) tento příklad:

void *ptr=malloc(1000);
@try { ... }
@catch (NSObject *o) {
  @throw; // v pořádku: paměť se uvolní také
} @finally { free(ptr); }

Ten je skutečně zcela v pořádku, vyplatí se však upozornit na nebezpečí, k němuž by mohla vést jeho příliš volná aplikace na jiné případy. Mnoho programátorů totiž jistě napadne, že s direktivou @finally je výhodný následující vzorec:

NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
@try {
  ...
} @finally {
  [pool release]; // POZOR, chyba!
}

Výhodný není, naopak, je to cesta do pekel! Pro pochopení, proč tomu tak je, stačí si uvědomit, že direktivy @try/@catch/@finally automaticky předají každou výjimku, jež není explicitně zachycena některou z direktiv @catch, na vyšší úroveň kódu. Z toho ovšem plyne, že výjimka, vytvořená v době platnosti našeho lokální autorelease poolu – a tedy uložená právě do něj – se předá "výše", ale ještě před tímto předáním se pool v bloku @finally uvolní. Odkaz na výjimku tedy již v době jeho použití nebude platný!

Není-li to zřejmé, pomůže přepsat si situaci do "starých" maker:

NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
NS_DURING
  ... [NSException exceptionWith:...] samozřejmě ...
  ... uloží výjimku do aktivního autorelease poolu ...
NS_HANDLER
  [pool release]; // ekvivalent "finally", pool se uvolní
  [localException raise]; // toto udělá @try/@catch ...
    // ... automaticky pro každou nezachycenou výjimku; ...
    // ... zde ovšem jasně vidíme, že (a proč) ...
    // ... po uvolnění poolu localException není validní!
NS_ENDHANDLER
[pool release]; // ekvivalent "finally"

Nejjednodušším řešením výše popsaného problému je... se o autorelease pool prostě nijak nestarat. Jen ať si výjimka vyskočí ven; pool se uvolní automaticky díky poolu nadřízenému.

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

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Začínáme s  

 » Rubriky  » Software  

 

 

 

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

 

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

Uživatelské jméno:

Heslo: