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:
Software
Irbis: že prý žádné novinky?
29. září 2009, 00.00 | Více než po roce se opět vracíme se seriálem o programování v Kakau – je zde nová kočka s novými možnostmi, a na ty se v následující sérii článků podíváme blíže.
Pro uživatele byl Irbis inserován jako „verse, jež nebude obsahovat žádné novinky“ (ale jen opravy chyb). Potěšující je, že z hlediska programátora tomu tak není – alespoň tedy co se týká novinek: Irbis je novými a velmi zajímavými technologiemi i drobnými, ale cennými vylepšeními existujících API přímo nabit.
(Zbývá jen doufat, že nebude také nabit novými chybami; po zkušenostech s Le Prdem mám poněkud obavy, ale klepu na dřevo a doufám. Mimochodem, závažná chyba, již jsme si ukázali v minulém článku, je již opravena; méně závažná chybka varování nikoli, ale to v zásadě asi moc nevadí, neboť překladač GNU C v Mac OS X stejně pěje píseň labutí – k tomu se ale vrátíme až později.)
Podíváme se na novinky a rozšíření samotného jazyka Objective C, na nové technologie a jim odpovídající knihovny, ukážeme si novinky ve Foundation i v Application Kitu, a pak… uvidíme: bude-li zájem, můžeme opět spustit pravidelný seriál o programování; témat je k dispozici dost a dost :)
Bloky? Bloky. Bloky!
Hned dnes se podíváme, co je nového v samotném jazyce. Moc toho není; v podstatě jde o jedinou změnu – ale o změnu velmi podstatnou a důležitou; osobně bych si dovolil ji označit za vůbec nejvýznamnější doplněk jazyka od doby, kdy Brad Cox implementoval objc_send: při programování v Mac OS X totiž nyní můžeme používat bloky.
Co je to blok
V kontextu klasických strukturovaných programovacích jazyků se občas pojem blok používal pro složený příkaz, který mohl obsahovat deklarace vlastních proměnných – třeba nějak takto:
void fnc(NSUInteger n) {
for (NSUInteger i=0;i<n;i++) {
printf("Toto se provede %lu-krát.\n",n);
}
}
„Opravdický blok“ – v angličtině se někdy pro odlišení užívá pojmu closure, který se nakolik je mi známo nedá nijak rozumně přeložit do češtiny, takže se budeme držet českého termínu „blok“ – je v podstatě totéž; jen se to může vzít jako samostatný celek a z postavení ve funkci „vyndat“ a použít jindy a jinde. Udělat ze složeného příkazu skutečný blok, je triviální – prostě před jeho otevírací složenou závorku přidáme znak ‚^‘. Blok můžeme třeba uložit do proměnné – blokové proměnné se deklarují přesně stejně, jako ukazatele na funkce, jen namísto ‚*‘ mají ‚^‘ – a jejím prostřednictvím zavolat, nějak takto:
...
void (^block)()=^{ // teď již je to opravdický blok!
printf("Toto se provede %d-krát.\n“,n);
}
for (i=a;i<b;i++) block();
...
Výše uvedený příklad ovšem nepřinesl nic zajímavého – jen ilustroval princip, na němž bloky fungují. Opravdu zajímavé to začne být ve chvíli, kdy bloky použijeme jako argumenty funkcí (či zpráv Objective C, ale to si ukážeme až za chvilku). Volající pak nebude samozřejmě ukládat blok do proměnné, ale přímo jej zapíše na místo argumentu volané funkce – nějak takto:
...
atexit_b(^{
printf("Program je ukončen, uklízíme...\n");
...
});
...
V tomto případě se zadaný blok – ať už je jeho obsahem cokoli – automaticky provede bezprostředně před ukončením programu.
Bloky a proměnné
Bloky mohou mít – přesně stejně jako funkce – argumenty (a také návratovou hodnotu). Blok, použitý v cyklu, by tak nejspíše měl jeden argument, jímž by byla hodnota řídící proměnné, nějak takto:
void fnc(NSUInteger count) {
void (^block)(NSUInteger)=^(NSUInteger n){
printf("%lu-tý průchod cyklem\n",n);
};
for (NSUInteger i=0;i<count;i++) block(i);
}
Ještě zajímavější je to, že bloky mají přístup ke všem proměnným, jež jsou dostupné ve chvíli jejich deklarace, a „zafixují“ si jejich hodnoty. Podívejme se na jednoduchoučkou modifikaci minulé ukázky, kdy blok nevytvoříme uvnitř funkce, ale předáme jí jej jako argument:
void fnc(NSUInteger count,void(^block)(NSUInteger)) {
for (NSUInteger i=0;i<count;i++) block(i);
}
a na následující příklad programu, který jí využije pro několikanásobný opis každého druhého ze svých vstupních argumentů (přičemž prvé argumenty určují počet opakování):
int main(int argc,char *argv[]) {
for (int an=1;an<argc;an+=2)
fnc(atoi(argv[an]),^(int n){printf("%d: %s\n",n+1,argv[an+1]);});
return 0;
}
~> cc -std=gnu99 q.m && ./a.out 2 dva 3 tri
1: dva
2: dva
1: tri
2: tri
3: tri
~>
Princip funkce je zřejmý – v cyklu procházíme vstupní argumenty; každý prvý z nich použijeme (po převodu na číslo pomocí standardní knihovní funkce atoi) jako počet opakování bloku. Uvnitř bloku pak vypíšeme pořadové číslo – jež blok dostane jako vstupní argument – a následující argument příkazového řádku.
A zde je vhodné se na chvilku zarazit: funguje to logicky tak, jak bychom čekali, vypisují se ty správné argumenty... ale jak se k nim blok, volaný z funkce fnc, dostane, když v ní nikde nejsou k dispozici ani seznam argumentů argv, ani pomocná proměnná an cyklu z funkce main?
To je právě ona schopnost bloků, o níž hovoříme: ve chvíli, kdy je blok definován, si zafixuje hodnoty všech proměnných, jež jsou v něm použity. Jinde, kde je volán (a kde tyto proměnné obecně nejsou přístupné), je pak blok může bez problémů použít. Jinde – nebo klidně i jindy! Prostudujte si následující příklad, a uvědomte si, co z něj pro široké možnosti práce s bloky vyplývá:
int main(int argc,char *argv[]) {
for (int an=1;an<argc;an+=2) {
int count=atoi(argv[an]);
if (count) fnc(count,^(int n){printf("%d: %s\n",n+1,argv[an+1]);});
else atexit_b(^{printf("argument #%d (%s) \n",an,argv[an+1]);});
}
printf("Konec funkce main.\n"); // nyní již proměnná 'an' neexistuje
return 0;
} // a nyní již neexistují ani argumenty 'argc' a 'argv'
~> cc -std=gnu99 q.m && ./a.out 0 konec 1 cosi
1: cosi
Konec funkce main.
argument #1 (konec)
~>
Podpora bloků v Mac OS X
Jednu ze služeb, jež využívá bloků, jsme si již ukázali – jde o novou standardní službu atexit_b, jež blok uloží a provede jej těsně před ukončením programu. API Irbisu podobných služeb nabízí mnoho, řadu na úrovni plain C, a další v objektových knihovnách Cocoa. Bezmála kdekoli, kde dává smysl „provést nějaký kus kódu jinde, jindy nebo jinak než právě teď“, můžeme pro daný účel velmi pohodlně využít právě bloků.
Snad nejvýraznějším příkladem je malý zázrak jménem Grand Central Dispatch – technologie, jež nejenže umožňuje psát efektivní paralelní kód o mnoho řádů snáze, než kdybychom implementovali vlákna pomocí služeb vhodné knihovny (např. NSThread v Cocoa); navíc jsou takto napsané programy daleko efektivnější. My se na ni podíváme později v samostatném článku podrobněji; vyplatí se ale ukázat si hned to, jak využívá bloků.
Nejjednodušším příkladem může být tento kód, který simuluje čtveřici časově náročných operací:
int main(int argc,char *argv[]) {
for (NSUInteger i=0;i<4;i++) {
printf("operace %lu...\n",i+1);
sleep(2);
printf("... operace %lu hotova\n",i+1);
}
return 0;
}
~> cc -std=gnu99 q.m && time ./a.out
operace 1...
... operace 1 hotova
operace 2...
... operace 2 hotova
operace 3...
... operace 3 hotova
operace 4...
... operace 4 hotova
./a.out 0.00s user 0.00s system 0% cpu 8.010 total
~>
Ten, kdo má nějaké zkušenosti s programováním s vlákny, ví, že převést tento triviální kód na paralelní běh by nebylo příliš těžké, ale rozhodně by bylo zapotřebí počet programových řádků minimálně zdvojnásobit. Díky blokům (a technologii Grand Central Dispatch) však stačí jen nahradit složený příkaz v těle cyklu blokem (tj. vlastně jen přidat ‚^‘ a řídící proměnnou), a nahradit příkaz for voláním vhodné funkce GCD:
int main(int argc,char *argv[]) {
dispatch_apply(4,dispatch_get_global_queue(0,0),^(size_t i){
printf("operace %lu...\n",i+1);
sleep(2);
printf("... operace %lu hotova\n",i+1);
});
return 0;
}
~> cc -std=gnu99 q.m && time ./a.out
operace 1...
operace 2...
... operace 1 hotova
operace 3...
... operace 2 hotova
operace 4...
... operace 3 hotova
... operace 4 hotova
./a.out 0.00s user 0.00s system 0% cpu 4.004 total
~>
(Mimochodem, proč se neprovedly všechny čtyři operace naráz, ale běžely po dvojicích? Proto, že techologie GCD dobře ví, že na počítači jsou právě dvě jádra, a nemá tedy smysl spouštět více než dvě vlákna /jež neužívají semaforů/ najednou.)
Ukažme si na závěr jen jednu z mnoha – opravdu z mnoha! – možností využití bloků v Objective C. Dejme tomu, že máme třeba pole objektů, representujících běžící aplikace, a že je chceme setřídit nějakým velmi absurdním způsobem, jenž by bylo obtížné nebo nemožné formulovat standardním způsobem s využitím objektů NSSortDescriptor – třeba chceme třídit podle třetího písmene názvu. Tradičně bychom samozřejmě mohli použít službu sortedArrayUsingFunction:; to ovšem znamená definovat statickou funkci na jiném místě, než kde třídíme – a to je nešikovné, nepohodlné a přináší to risiko chyb při údržbě a změnách kódu.
S bloky není nic snazšího; vypadalo by to – včetně ochrany pro případ, že některý z názvů není dostatečně dlouhý – asi nějak takto:
NSArray *sorted=[[[NSWorkspace sharedWorkspace] runningApplications] sortedArrayUsingComparator:
^NSComparisonResult(id a,id b){
if ([a=[a localizedName] length]<3 || [b=[b localizedName] length]<3) return NSOrderedSame;
return [a characterAtIndex:2]-[b characterAtIndex:2];
}];
Za povšimnutí stojí kompletní deklarace bloku včetně typu jeho návratové hodnoty („^NSComparisonResult(id a,id b)“) – v takovýchto případech to je zapotřebí, neboť překladač nemůže s jistotou návratovou hodnotu odhadnout z kódu, a výsledkem může být chyba.
Obsah seriálu (více o seriálu):
- Nastal čas na kakao...
- Tak nejdřív kakao ochutnáme...
- Programovací jazyk C: velmi, velmi stručně
- Objective C: to si vysvětlíme podrobněji
- Co jsme si o Objective C ještě neřekli...
- Nastal čas na kakao - Vznik a zánik objektů
- Nastal čas na kakao - Kopírování objektů
- Nastal čas na kakao - Skryté podtřídy
- Nastal čas na kakao - Základní služby objektů
- Nastal čas na kakao - Jak správně psát v Objective C
- Nastal čas na kakao - Jak správně importovat
- Nastal čas na kakao - Podtřídy, delegáti, vkládání, jak se to rýmuje?
- Nastal čas na kakao - Využití kategorií namísto dědičnosti
- Nastal čas na kakao - Vkládání objektů a přesměrování zpráv
- Nastal čas na kakao - Inicializace a rušení objektů
- Nastal čas na kakao - Metody initWith... a designovaný inicializátor
- Nastal čas na kakao - Inicializace: tipy a triky
- Nastal čas na kakao - Accesory: přístup k proměnným instancí
- Nastal čas na kakao - Šedá je teorie, zelený je strom života...
- Nastal čas na kakao - Více o XCode: inspektory
- Nastal čas na kakao - Aplikace RSS2: datový model
- Nastal čas na kakao - Aplikace RSS: implementace datového modelu
- Nastal čas na kakao - Aplikace RSS: parsování XML
- Nastal čas na kakao - Interface Builder a uživatelské rozhraní
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Druhý kontrolér a dokončení aplikace
- Nastal čas na kakao - Drobná vylepšení a zdokonalení...
- Nastal čas na kakao - Ladění
- Nastal čas na kakao - Třídy Foundation Kitu
- Nastal čas na kakao - Třídy Foundation Kitu (2)
- Nastal čas na kakao - Textové řetězce: NS(Mutable)String
- Nastal čas na kakao - Čísla, binární data a další...
- Nastal čas na kakao - Archivace objektů
- Nastal čas na kakao - Trocha magie, aneb distribuované objekty
- Nastal čas na kakao - Málem bychom zapomněli: NSAutoreleasePool
- Nastal čas na kakao - Zpracování výjimek: NSException
- Nastal čas na kakao - NSInvocation a černá magie
- Nastal čas na kakao - Kakao v Tygrovi
- Nastal čas na kakao - Notifikace: nepřímé předávání zpráv
- Nastal čas na kakao - NSUserDefaults
- Nastal čas na kakao - Co nového ve Foundation Kitu
- Nastal čas na kakao – s Intelem, s Intelem, jedeme do...
- Co nového v Xcode
- Začínáme s AppKitem
- Jak MVC v Kakau vypadá doopravdy?
- Jak MVC v Kakau vypadá doopravdy: dokončení
- Přehled tříd AppKitu
- Nastal čas na kakao - Přehled tříd AppKitu 2
- Přehled tříd AppKitu 3: zbývající třídy GUI
- Přehled tříd AppKitu 4: textový systém
- Nastal čas na kakao - Přehled tříd AppKitu 5: hlavně grafika
- Přehled tříd AppKitu 6: dokumentový systém
- Přehled tříd AppKitu 7: dokončení
- Pojmenované vlastnosti objektů
- Pojmenované vlastnosti objektů: implementace
- Pojmenované vlastnosti objektů: relace 1:N
- Pojmenované vlastnosti objektů: řazení jmen a agregační funkce
- Sledování změn objektů
- Sledování změn objektů – ukázka
- Sledování změn objektů – zdrojový kód
- Sledování změn objektů: kód modelu
- Sledování změn objektů: přímý přístup
- Kontroléry a vazby
- Vázání vazeb
- Další vazby s jednoduchým kontrolérem
- Implementace a použití převodu hodnot
- Validace hodnot
- Validace a chyby, a jedna hezká vazba...
- Práce s polem objektů
- Základní vazby NSArrayControlleru
- Převodníky, přepínače, placeholdery
- Mírná vylepšení v mezích zákona
- Objective C 2.0 - novinky z Leoparda
- NSTreeController
- Programování v Cocoa - Pár tipů a triků
- Programování v Cocoa - Základy kreslení
- Kterak nakreslit modrý obdélník...
- Další služby pro kreslení
- Obrázky a písmenka...
- Události a myš
- Lepší práce s myší
- Události klávesnice
- Input Management
- Příkazy a schránka
- Další události
- Táhni a padni
- Byli jsme na tahu; nyní padneme.
- Zvolme si, jak vhodit
- Drobnosti a chybičky
- Speciální případy tahání či házení
- Kterak táhnout něco, co neexistuje?
- Jak na sítě...
- NSURLConnection
- Safari za minutu
- Služby WebKitu
- Kakao v Leopardu
- Druhé Objective C
- Druhé Objective C: různé drobnosti
- Druhé Objective C: kategorie a protokoly
- Druhé Objective C: nový příkaz cyklu
- Druhé Objective C: atributy a accesory
- Druhé Objective C: atributy a accesory
- 64 je dvakrát 32
- Ubicumque dulce est, ibi et acidum invenies...
- Irbis: že prý žádné novinky?
- Blok sem, blok tam, nám už je to všechno jasné...
- Bloky jsou i v AppKitu
- Irbis a Foundation Kit
- Kde jsou má data?
- Kde jsou má data? V NSCache!
- Soubor, jméno, URL, jak se to rýmuje...
- Další podpora NSURL
- Zabíjení!
- A máme tady i...OS!
- Systémové prvky GUI
- Programování pro iOS 1. díl - Rozdíly mezi "i" a "Mac"
- Programování pro iOS - 2. Začínáme programovat
- Programování pro iOS - 3. základní ovladače a propojení GUI s kódem
- Programování pro iOS - 4. Varovná hlášení
- Programování pro iOS - 5. Rámce a jejich řídicí objekty
- Programování pro iOS - 6. Ukládání dat
- Programování pro iOS - 7. Správa paměti a starý restík
- Programování pro iOS - 8. Dokončení aplikace
- Programování pro iOS - 9. Jak dostat aplikaci do iPhone
- Programování pro iOS - 10. Instalace aplikace do cizího iPhone
- Programování pro iOS - 11. Jak dostat aplikaci do libovolného iPhone
- Programování pro iOS - 12. Touching!
- Programování pro iOS - 13. Kreslíme na iPhone
- Programování pro iOS - 14. Udělejme gesto
- Programování pro iOS - 15. Další gesta
- Programování pro iOS - 16. Více prstů, více zábavy
- Programování pro iOS - 17. Podpora standardních gest
- Programování pro iOS - 18. Recognizery v iOS
- Programování pro iOS - 19. Další standardní recognizery
- Programování pro iOS - 20. Co nového v iOSu
- Programování pro iOS - 21. "Multitasking"
- Programování pro iOS - 22. Nulla est honesta avaritia nisi temporis
- Programování pro iOS - 23. Jak se aktivovat, jsme-li v pozadí
- Programování pro iOS - 24. Zbývající drobnosti
- Programování pro iOS - 25. Řídicí objekty rámců
- Programování pro iOS - 26. Jak se dělá UIViewController
- Programování pro iOS - 27. Kde vzít rámce
- Programování pro iOS - 28. Základní služby
- Programování pro iOS - 29. Práce s rámci
- Programování pro iOS - 30. Rotace zařízení
- Programování pro iOS - 31. Správa paměti v rámcích
- Programování pro iOS - 32. Řídicí objekt pro tabulky
- Programování pro iOS - 33. Řídicí objekt pro strom
- Programování pro iOS - 33. Více o UINavigationControlleru
- Programování pro iOS - 35. Ještě jednou UINavigationController
- Programování pro iOS - 36. Po navigátoru taby
- Programování pro iOS - 37. Více o UITabBarControlleru
- Programování pro iOS - 38. Dokončení UITabBarControlleru
- Programování pro iOS - 39. UIPopoverController
- Programování pro iOS - 40. Další triky UIPopoverControlleru
- Programování pro iOS - 41. Zbývající služby UIPopoverControlleru
- Programování pro iOS - 42. UISplitViewController
- Programujeme v
iTunesXcode 4 - Programování pro iOS - 44. Předvolby Xcode 4
- Programování pro iOS - 45. Práce v Xcode 4
- Xcode 4: projekt a cíle
- Xcode 4: práce s cíli
- Xcode 4: Build Settings
- Xcode 4: Build Phases
- Xcode4: Build Phases podruhé
- Xcode 4: Co jsou to Build Rules?
- Xcode4: taje editoru
- Xcode4: automatické doplňování v editoru
- XIBy chyby
- Více o XIBech
- Editor XIBů
- Inspektory pro XIBy
- Vazby mezi objekty v XIBech
- Vazby mezi objekty v kódu
- Paletky Xcode pro XIBy
- Xcode 4: levý sloupec
- Xcode 4: okno Organizer
- Xcode 4: okno Organizer, část druhá
- Xcode 4: co je to Workspace?
- Xcode 4: základy schémat
- Xcode 4: akční schémata