Obsah | Další |
V prvním příkladu si ukážeme nejjednodušší příklad OOP, spíše 'programování s objekty' než skutečně objektové programování: vytvoříme jednoduchý program, který využívá hotových tříd (z knihoven Cocoa) podobně, jako neobjektové programy využívají knihovních služeb.
Program bude pracovat jako velmi jednoduchá databáze, ukládající dvojice textových řetězců; první z textů bude sloužit jako klíč pro vyhledávání, druhý reprezentuje hodnotu. Využijeme zcela triviální uživatelské rozhraní na úrovni příkazové řádky; pro ukládání dat použijeme standardní třídu NSMutableDictionary.
// Objective C -- příklad 1
//
// využití předdefinovaných tříd (knihovny tříd Cocoa)
#import <Foundation/Foundation.h>
#define STOREFILE @"/tmp/sample1.data"
void main()
{
NSAutoreleasePool *pool=[NSAutoreleasePool new]; // garbage collector
NSMutableDictionary *seznam; // objekt, reprezentující dvojice řetězců
char buffer[512]; // vstupní textový buffer
if ((seznam=[NSMutableDictionary dictionaryWithContentsOfFile:STOREFILE])==nil)
seznam=[NSMutableDictionary new]; // nemohu-li otevřít soubor, vytvořím prázdný seznam
printf("V seznamu je %d položek\n\n",[seznam count]);
// jednoduchá nápověda (uživatelské rozhraní programu by samozřejmě
// zasluhovalo vylepšení):
printf("klíč,hodnota / ?klíč / * / !\n");
// hlavní cyklus, do příkazu switch vstoupí každý zadaný řádek
for (;gets(buffer);) switch (*buffer) {
// vykřičník program ukončí
case '!': goto Konec;
// hvězdička opíše všechny uložené dvojice
case '*': {
NSEnumerator *en=[seznam keyEnumerator];
id key;
printf("Tabulka obsahuje:\n");
while (key=[en nextObject])
printf("\t%s, %s\n",[key cString],[[seznam objectForKey:key] cString]);
}
break;
// otazník vyhledá hodnotu k zadanému klíči
case '?': {
NSString *val=[seznam objectForKey:[NSString stringWithCString:buffer+1]];
if (!val) printf("Není v seznamu!\n");
else printf("> %s\n",[val cString]);
}
break;
// jinak vkládáme novou dvojici
default: {
NSString *val=[NSString stringWithCString:buffer];
// rozdělíme na dva řetězce tam, kde je čárka
NSArray *a=[val componentsSeparatedByString:@","];
// a zapíšeme do seznamu
[seznam setObject:[a objectAtIndex:1] forKey:[a objectAtIndex:0]];
break;
}
}
Konec:
[seznam writeToFile:STOREFILE atomically:YES];
[pool release];
printf("hotovo\n");
}
// end of file
Podívejme se podrobněji na jednotlivé části programu:
Použití direktivy #import namísto 'obyčejné céčkové' direktivy #include nás zbaví problémů s případným vícenásobným překladem vnořených hlavičkových souborů. V tomto případě je samozřejmě lhostejné, kterou direktivu použijeme, protože standardní 'céčkové' hlavičkové soubory jsou proti vícenásobnému překladu ochráněny pomocí vložených direktiv #ifdef (direktiva #import pouze o něco málo zrychlí překlad).
Cocoa se v plnohodnotných aplikací automaticky stará o garbage collector (uvolňování objektů, které již nepotřebujeme). Vzhledem k tomu, že zde ale nemáme plnohodnotnou aplikaci, ale jen jednoduchoučký řádkový program, musíme garbage collector vyrobit sami (a na konci programu zase uvolnit příkazem [pool release]).
S funkcí třídy NSAutoreleasePool se seznámíme později, až budeme popisovat služby knihovny Foundation Kit; prozatím jen ve stručnosti uvedeme, že se postará o korektní uvolnění všech objektů, které v prámci programu automaticky vznikly.
Proměnná seznam je typu NSMutableDictionary*, je tedy určena pro objekty této třídy, a překladač by vydal varování kdybychom do ní uložili objekt jiné třídy, nebo kdybychom jí posílali zprávy, jimž objekty třídy NSMutableDictionary nerozumějí. Je však vhodné si uvědomit, že přesto můžeme do proměnné uložit objekt libovolné třídy a můžeme mu posílat jakékoli zprávy -- překladač jen vydá varování, ale vše přeloží zcela korektně.
Požadovaný objekt třídy NXStringTable vytvoříme tak, že třídě pošleme zprávu 'new'; třída vytvoří objekt a vrátí jeho identifikátor. Ten uložíme do proměnné 'seznam'.
Stejně jako většina ostatních "datových" tříd Foundation Kitu, dokáže i NSMutableDictionary vytvořit nový objekt na základě dat, uložených v souboru; to si vyžádáme zprávou dictionaryWithContentsOfFile:. Pokud se to nepodaří -- nebylo možné vytvořit objekt podle souboru (např. proto, že soubor neexistuje), takže zpráva vrátí nil -- vytvoříme prázdný seznam zprávou new (to se podaří vždy, leda by nebyl dostatek paměti).
Nakonec informujeme uživatele o počtu položek v seznamu; počet nám objekt sdělí pošleme-li mu zprávu count.
Uvnitř příkazu switch, rozlišujícího různé formy vstupu, se nejprve podíváme na obsah větve default:, která nám umožňuje vkládat do databáze nové dvojice údajů. První zpráva [NSString stringWithCString:buffer] jen vytvoří objekt třídy NSString na základě obsahu zadaného bufferu (to je mimochodem jeden z těch mnoha objektů, jež za nás automaticky uvolní garbage collector pool).
Oba zadané řetězce jsou odděleny čárkou. Použijeme proto standardní zprávu componentsSeparatedByString:; jakýkoli textový objekt na jejím základě vytvoří pole všech svých částí, jež jsou odděleny zadaným separátorem.
Pak už jen vložíme klíč a jemu odpovídající hodnotu do databáze příkazem setObject:forKey:; jednotlivé textové řetězce z pole získáme pomocí "indexovací" zprávy objectAtIndex:.
Zadáme-li řetězec začínající otazníkem, vyžádá si program od objektu seznam vyhledání hodnoty odpovídající klíči ve zbytku vstupního řetězce. Seznam tuto službu vykoná po přijetí zprávy objectForKey:.
Samozřejmě, že vrácená hodnota je opět objekt; abychom ji mohli zobrazit standardním příkazem printf, který s objektyu pracovat neumí, převedeme jeho obsah na obyčejný "Céčkový" string zprávou cString.
Všechny kontejnerové objekty Foundation Kitu umožňují i sekvenční procházení. Využívá se pro něj objekt NSEnumerator; ten vlastně reprezentuje stav procházení (tj. 'jak daleko jsme zatím došli').
Nakonec pošleme objektu seznam zprávu writeToFile:atomically:, aby zapsal svůj nový obsah do souboru. Atribut atomically určuje, zda se má zápis provést "transakčně" (tj. buď se kompletně podaří, nebo zůstane v souboru původní obsah), nebo ne.
Obsah | Další |
Copyright (c) Chip, O. Čada 2000