Pojmenované vlastnosti objektů: relace 1:N - 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

Pojmenované vlastnosti objektů: relace 1:N

26. ledna 2006, 00.00 | Relace 1:N má vlastní, mírně odlišné API: je representována polem (pro relaci s daným pořadím) či množinou (pro nesetříděnou relaci) objektů. Relací 1:N by např. mohl být seznam knih daného autora – pak by [autor valueForKey:@"knihy"] vrátilo objekt třídy NSArray, jehož prvky by byly všechny knihy daného autora.

V našem dosavadním popisu toho, jak funguje systém KVC (Key/Value Coding), jsme se zatím příliš nezabývali tím, jakého typu mohou být hodnoty, vracené prostřednictvím zprávy valueForKey:, resp. nastavované pomocí zprávy setValue:forKey: – víme jen, že to může být buď neobjektová hodnota (která je automaticky korektně "zabalena" do objektu třídy NSNumber či NSValue) nebo objekt.

Ve skutečnosti systém KVC rozlišuje tři případy:

  • atributy jsou takové hodnoty, které patří objektu samotnému, a jsou fakticky nebo alespoň funkčně jeho součástí. Tak tomu bylo ve všech příkladech minule – patří sem jak neobjektové hodnoty, tak i vlastní objektové atributy, které jsou kopírovány (příkladem byl textový titulek);
  • relace 1:1 se nazývají odkazy na jiné, vnější objekty. Rozdíl spočívá v tom, že zatímco atributy má každý objekt vlastní, v relacích mohou být objekty měnitelné a sdílené. Tento rozdíl je zásadní z hlediska designu aplikace, ale není příliš významný z hlediska API – služby KVC pracují s atributy i s relacemi 1:1 přesně týmž způsobem;
  • relace 1:N naproti tomu má vlastní, mírně odlišné API: je representována polem (pro relaci s daným pořadím) či množinou (pro nesetříděnou relaci) objektů. Relací 1:N by např. mohl být seznam knih daného autora – pak by [autor valueForKey:@"knihy"] vrátilo objekt třídy NSArray, jehož prvky by byly všechny knihy daného autora.

Práce s relacemi 1:N

Pokud je relace 1:N neměnná, žádné speciální API pro ni není zapotřebí – stačí opět použít zprávu valueForKey:, která vrátí objekt třídy NSArray nebo NSSet, obsahující všechny objekty, s nimiž je příjemce zprávy v relaci, a to je vše.

Jiná situace nastane v případě, že se obsah relace může měnit – jinými slovy, že lze přidávat a odebírat objekty. Nemůžeme se zde spolehnout na to, že zpráva valueForKey: vždy vrátí objekt třídy NSMutableArray nebo NSMutableSet, jehož obsah bychom mohli měnit; to proto, že ve skutečnosti mohou být relace uloženy jinak (třeba v NSMutableDictionary – takový příklad si ukážeme za chvilku) a pohled na "pole" či "množinu" může být generován dynamicky v rámci accesorů.

Zde proto systém KVC nabízí dvě pomocné zprávy, které práci s takovýmito relacemi výrazně usnadní:

-(NSMutableArray*)mutableArrayValueForKey:(NSString*)key;
-(NSMutableSet*)mutableSetValueForKey:(NSString*)key;

Zprávy vždy vrátí representaci relace, již můžeme přímo měnit (pomocí standardních metod pro práci s objekty tříd NSMutableArray a NSMutableSet), a knihovní kód se postará o to, aby se změny korektně promítly do skutečné relace objektu.

Systém KVC kromě toho nabízí pro práci s relacemi 1:N řadu dalších speciálních služeb: ukážeme si je příště, až se budeme zabývat tzv. řazením jmen (key paths). Dnes se podíváme trochu blíže na implementaci relací 1:N.

Základní implementace: opět vůbec žádná práce...

V nejjednodušším případě stačí, aby náš objekt obsahoval (resp. generoval prostřednictvím accesorů) objekty tříd NS(Mutable)Array, resp. NS(Mutable)Set – vše bude fungovat automaticky a korektně:

@interface Model:NSObject {
  NSMutableArray *r1;
}
@end
@implementation Model
-(void)dealloc {
  [r1 release];
  [super dealloc];
}
-(NSArray*)r2 {
  return [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
}
@end

Vše bude korektně fungovat; prostřednictvím zprávy valueForKey: s argumentem @"r1" nebo @"r2" získáme obsah kterékoli z relací; prostřednictvím zprávy mutableArrayValueForKey:@"r1" získáme přístup k obsahu relace r1, jenž můžeme měnit (v tomto speciálním případě by samozřejmě valueForKey:@"r1" fungovalo stejně dobře, neboť relace je uložena skutečně přímo v proměnné NSMutableArray). Povšimněme si, mimochodem, že knihovní kód dokonce objekt podle potřeby vytvoří – nemusíme tedy implementovat ani metodu init. Musíme ovšem vytvořené pole zrušit v metodě dealloc.

Generovaná relace

Dejme tomu, že z nějakého důvodu chceme společně s každým objektem z relace udržovat informaci o tom, kdy byl do relace uložen: optimální implementací pak asi bude NSMutableDictionary, v němž klíči budou vlastní objekty, a hodnotami instance NSDate representující moment, v němž byl objekt do relace vložen.

V takovém případě ovšem samozřejmě musíme implementovat accesory, které zajistí k relaci přístup – na základě toho, co o systému KVC dosud víme, by mohly vypadat třeba takto:

@interface Model:NSObject {
  NSMutableDictionary *md;
}
@end
@implementation Model
-init {
    if (!(self=[super init])) return nil;
    md=[[NSMutableDictionary alloc] init];
    return self;
}
-(void)dealloc {
  [md release];
  [super dealloc];
}
-(NSArray*)foo {
    return [md allKeys];
}
-(void)setFoo:(NSArray*)a {
    [md removeAllObjects];
    for (id o,en=[a objectEnumerator];o=[en nextObject];)
        [md setObject:[NSDate date] forKey:o];
}
@end

S takovouto implementací bude vše (při použití zprávy mutableArrayValueForKey:@"foo") fungovat korektně; nicméně, při každé změně bude knihovní kód nucen získat momentální obsah relace pomocí accesoru foo, vytvořit jeho měnitelnou kopii pomocí zprávy mutableCopy, změnu provést, a výsledek zapsat zpět do relace pomocí accesoru setFoo: – je zřejmé, že by to bylo dost neefektivní.

Speciální přímé accesory

Systém KVC proto umožňuje implementovat namísto setFoo: pomocné metody insertObject:inFooAtIndex: a removeObjectFromFooAtIndex:, které provedou patřičné změny přímo, vyžívajíce konkrétní implementace toho, jak jsou data uložena pro větší efektivitu.

Vzhledem k tomu, že naše relace však je principiálně nesetříděná (a také k tomu, že jsme si dosud nesetříděnou relaci neukázali), užijeme tentokrát rozhraní založené na množinách. I pro něj dokáže KVC využít speciální accesory pro větší efektivitu – addFooObject: a removeFooObject:, jejichž implementace by mohla vypadat třeba takto:

-(void)addFooObject:o {
    [md setObject:[NSDate date] forKey:o];
}
-(void)removeFooObject:o {
    [md removeObjectForKey:o];
}

Pak samozřejmě nemusíme implementovat setFoo:, a práce s modelem bude – při použití přístupové metody mutableSetValueForKey:@"foo" – mnohem efektivnější. Mimochodem – ačkoli výše uvedená implementace accesoru foo bude díky polymorfismu fungovat, měli bychom ji správně změnit na

-(NSSet*)foo {
    return [NSSet setWithArray:[md allKeys]];
}

Nakonec jen pro úplnost poznamenejme, že KVC nabízí ještě další, sofistikovanější možnosti; těmi se už ale na naší základní úrovni zabývat nebudeme.

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: