ObsahDalší

Objective C: příklad 1
Využití předdefinovaných tříd


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:

1. Import hlavičkových souborů

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).

2. Proměnná pool a NSAutoreleasePool

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.

3. Proměnná seznam a její deklarace

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'.

4. Otevření souboru nebo vytvoření prázdného seznamu

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.

5. Vkládání údajů do databáze

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:.

6. Vyhledávání údajů v databázi

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.

7. Prohledání celé databáze

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').

8. Ukončení programu

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.


ObsahDalší

Copyright (c) Chip, O. Čada 2000