Validace hodnot - 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:

Soutěž

Sponzorem soutěže je:

IDIF

 

Jaký fotograf/ka získal/a cenu za nejpopulárnější příspěvek v Nikon Photo Contest?

V dnešní soutěži hrajeme o:

Seriály

Více seriálů



Informace

Validace hodnot

14. června 2006, 00.00 | Dnes si ukážeme další velmi důležitou techniku, jež sice v zásadě patří do KVC, ale prakticky použitelná a významná se stává právě až ve chvíli, kdy modelové objekty podporující KVC začneme používat prostřednictvím vazeb spolu se standardními objekty grafického uživatelského rozhraní Cocoa – proto jsme se o ní ostatně nezmínili dříve. Jedná se o systém automatických validací hodnot atributů.

Minule jsme se naučili implementovat – a prostřednictvím vazeb používat – převodníky hodnot. Dnes si ukážeme další velmi důležitou techniku, jež sice v zásadě patří do KVC, ale prakticky použitelná a významná se stává právě až ve chvíli, kdy modelové objekty podporující KVC začneme používat prostřednictvím vazeb spolu se standardními objekty grafického uživatelského rozhraní Cocoa – proto jsme se o ní ostatně nezmínili dříve. Jedná se o systém automatických validací hodnot atributů.

Nejprve přidáme do modelu text

Vrátíme se k předposlednímu příkladu atributů, který jsme si ukázali v textech, objasňujících mechanismus a funkci KVO – totiž k atributům firstname, surname a fullName – a přidáme je do modelu; použijeme poslední implementaci z onoho příkladu – do rozhraní naší modelové třídy Test tedy přidáme deklarace proměnných firstname a surname, takže rozhraní nyní bude vypadat takto:

@interface Test:NSObject {
  float width,height;
  NSString *firstname,*surname;
}
@end

Do rozhraní vedle již existujících metod setWidth: a setHeight: přidáme nově následující metody, převzaté z příkladu:

@implementation Test
  ...
-(void)setFirstname:(NSString*)name {
  [firstname autorelease];
  firstname=[[name capitalizedString] retain];
}
-(NSString*)fullName {
  return [NSString stringWithFormat:@"%@ %@",firstname,surname];
}
-(void)setFullName:(NSString*)name {
  NSArray *a=[name componentsSeparatedByString:@" "];
  [self setFirstname:[a objectAtIndex:0]];
  [self setValue:[a lastObject] forKey:@"surname"];
}

+(void)initialize {
  [self setKeys:
    [NSArray arrayWithObjects:@"firstname",@"surname",nil]
    triggerChangeNotificationsForDependentKey:@"fullName"];
}
@end

V Interface Builderu pak přidáme do okna třeba ještě jeden formulář, s přidržením přepínače Alt jej zvětšíme na tři řádky, vhodně upravíme jejich titulky, a navážeme je postupně jejich vazbou "value" na všechny tři nové textové atributy, tedy na firstname, surname a na fullName – konkrétní postup si už snad na snímcích obrazovky ukazovat nemusíme, neboť je zřejmý.

Jenže nám to nefunguje tak úplně...

Když aplikaci přeložíme a vyzkoušíme, zjistíme, že víceméně vše funguje, jak má – můžeme vkládat nezávisle na sobě jména a korektně se generuje plné jméno (zajisté, v produkčním kódu bychom se obtěžovali hlídat hodnoty nil, takže by se nám v poli pro plné jméno neobjevovaly texty "(null)"; také bychom měli ověřit, zda v žádném ze jmen nejsou mezery – v těchto jednoduchých příkladech se na to ale můžeme pokojně vyabstrahovat:), můžeme vložit plné jméno a korektně se z něj vygenerují jednotlivé části; jakkoli vložíme křestní jméno, jeho první písmeno se zvětší.

Jenže ouha: editor, v němž jsme to křestní jméno vložili, o tom "neví"! Zadáme-li křestní jméno s malým písmenem v textovém poli "First name", zůstane v něm malé písmeno (ačkoli, jak dokládá "Full name", ve skutečnosti je jméno v datovém modelu uloženo správně). Podobně, vložíme-li jméno s malým prvním písmenem prostřednictvím pole "Full name", zůstane špatná hodnota tam (tentokrát fakt, že do modelu se jméno uložilo správně opraveno, indikuje samozřejmě pro změnu pole "First name", jež svou hodnotu přebralo z modelu prostřednictvím kontroléru na základě notifikace KVO po změně):

To je proto, že jsme accessor setFirstname: implementovali špatně! Není totiž úkolem accessoru kontrolovat a upravovat – neřkuli snad dokonce odmítnout! – vloženou hodnotu; od toho jsou nezávislé samostatné validační metody.

Validace atributů

Jedna ze služeb systému KVC "pro starší a pokročilé", o níž jsme se dosud nezmínili, spočívá v tom, že systém KVC definuje standardní rozhraní pro validace atributů: pro jakýkoli atribut "xyz" můžeme v modelu definovat metodu validateXyz:error:, jež hodnotu atributu ověří, podle potřeby změní, nebo – pokud je zadaná hodnota neopravitelná – ohlásí chybu.

Dříve jsme se o tom nezmiňovali hlavně proto, že v samotném systému KVC jsou validace poněkud k ničemu: nikdo a nic je automaticky nepoužívá, programově klidně můžeme nastavit nevalidní hodnoty, a "všem to bude docela jedno". Jiná věc ovšem je, pokud prostřednictvím systému vazeb spojíme model (resp. kontrolér, který se o něj stará) s některým ze standardních objektů grafického uživatelského rozhraní Cocoa – ty totiž všechny berou validace v úvahu.

Můžeme začít tím, že implementujeme jednoduchou validaci, jež pouze zvětší prvé písmeno jakéhokoli textu, vloženého do atributu "firstname" – a kromě toho samozřejmě také odstraníme chybnou změnu atributu uvnitř accessoru setFirstname:

-(BOOL)validateFirstname:(id*)value error:(NSError**)error {
    *value=[*value capitalizedString];
    return YES;
}
-(void)setFirstname:(NSString*)name {
    [firstname autorelease];
    firstname=[name copy];
}

Validace fungují tak, že editor (v našem případě textové pole, opět samozřejmě prostřednictvím kontroléru) automaticky před ukončením editace zavolá validační metodu a předá jí referencí v argumentu value hodnotu, již má v úmyslu uložit do modelu. Validační metoda pak má tři možné vzorce chování:

  • může prozkoumat navrženou hodnotu a zjistit, že je správná. Pak již nemusí dělat nic dalšího a jen vrátí hodnotu YES. My se v našem jednoduchém příkladu touto variantou neobtěžujeme;
  • může navrženou hodnotu nahradit jinou, správnou hodnotou – proto je hodnota předána referencí (id*) a nikoli přímo. I v tomto případě metoda vrátí YES; editor ovšem snadno zjistí, že po volání validace se obsah argumentu value liší, a do modelu uloží hodnotu novou. Tu také sám zobrazí;
  • zjistí-li metoda, že hodnota není správná a nelze ji opravit, uloží do argumentu error (také předaného referencí – NSError**) popis chyby zabalený do instance standardní třídy NSError, a vrátí NO. To v našem případě nenastává (ale ukážeme si takový příklad příště).

Ještě musíme v Interface Builderu ověřit, zda jsou u všech tří vazeb nastaveny přepínače "Validate Immediately". My si o nich řekneme trochu více, až se budeme zabývat kontrolérem třídy NSTreeController; prozatím se spokojíme s tím, že má-li validace v našem případě fungovat, musí být tento přepínač zapnutý.

Pokud jste snad po přidání výše uvedené validační metody a ověření NIBu ukvapeně zbuildovali a spustili aplikaci, vidíte, že se chování trochu změnilo, ale dosud není zcela správné: textové pole "First name" již funguje přesně jak má (tedy vložíme-li do něj text s malým písmenem na začátku, ihned po opuštění textového pole se písmeno automaticky zvětší). Zato však vkládání prostřednictvím pole "Full name" nezvětšuje písmeno vůbec... to je ale přece samozřejmé, vždyť jsme si před chvilkou jasně řekli, že programově klidně můžeme nastavit nevalidní hodnoty, a "všem to bude docela jedno" – a my nastavujeme hodnotu atributu "firstname" programově z metody setFullName:. Chceme-li validovat i atribut "fullName", musíme doplnit validační metodu i pro něj – ta by mohla vypadat v nejjednodušším případě třeba takto:

-(BOOL)validateFullName:(id*)value error:(NSError**)error {
    NSArray *a=[*value componentsSeparatedByString:@" "];
    NSString *s=[a objectAtIndex:0];
    if (![self validateFirstname:&s error:error]) return NO;
    *value=[NSString stringWithFormat:@"%@ %@",s,[a lastObject]];
    return YES;
}

Funkce nové metody je snad zřejmá, prostě pro validaci prvé části jména použijeme již hotovou metodu validateFirstname:error: (nejde jen o to, abychom si ušetřili práci; ještě mnohem důležitější je konsistence – pokud v budoucnosti validaci křestního jména nějak změníme, aplikuje se díky této implementaci změna i na validaci celého jména, což je samozřejmě správně).

Pokud nyní aplikaci zbuildujeme a spustíme, bude vše fungovat jak má: ať již vložíme křestní jméno prostřednictvím kteréhokoli pole, jeho prvé písmeno se automaticky zvětší – nejpozději ve chvíli, kdy textové pole opouštíme.

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

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Software  

 

 

 

 

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

Uživatelské jméno:

Heslo: