KVC i Cocoa

Ibland lär man sig saker långsamt och inser inte att man faktiskt insett något. Nu slog det mig att en kort genomgång av Key-Value Coding i Cocoa kanske kan hjälpa andra. Alltså: en rekapitulation av vad KVC är, och varför det är bra.

Ola Bäckström 19 juni 2008

KVC är i korta drag att man kan fråga ett objekt om dess attribut (properties) med hjälp av en nyckel i form av en sträng.

Som exempel tar vi ett objekt myMessage av typen

// code assumes obj-c 2.0
@interface TTMessage  {
	BOOL highPriority;
	NSString *messageText;
}
@property BOOL highPriority;
@property(copy) NSString* messageText;

@end

Klassisk obj-c

Man kan som vanligt fråga

[myMessage highPriority] 

och få svaret YES eller NO. Man kan också sätta värdet med

[myMessage setHighPriority:YES] 

Här används meddelandesändning, en selektor anger vilken metod som ska utföras på myMessage. I princip är selektorn ett heltal som sedan används för att slå upp var själva funktionen ligger i minnet. Det hela går ganska snabbt, bl a cachar objective-C runtimen vanliga selektor-uppslag.

KVC innebär att man kan nå setters&getters med strängar.

I stället för att ange en selector så anger man en sträng "highPriority". KVC-mekaniken tar hand om att slå upp och finna rätt selektor för att sedan anropa settern eller gettern. Det hela bygger på att man följer KVC-namnkonvention för dessa. Ett attribut x ska ha gettern x och settern setX. Som tur är får man automatiskt rätt namngivna getter och setters om man använder Objective-C 2.0 property-direktivet.

Exempel

[myMessage setValue:newText forKey:@"messageText"];

Där newText är en sträng och där nyckelni detta exempel är hårdkodat som en statisk sträng. Det är inte ett krav, utan finessen ligger naturligtvis i att dessa strängar kan tänkas komma från annat håll. Ett exempel är att det kommer från nib-filen som gjorts i ordning i Interface Builder. Ett lite krystat exempel:

[myMessage setValue:newText forKey:[NSString initWithString:@"messageText"]];

En annan anmärkning är att man får ett boxat (instoppat i en NSNumber) svar om propertien är någon av de enkla datatyperna (heltal, flyttal, NSPoint etc):

NSNumber *prio = [myMessage valueForKey:@"highPriority"];

Resultatet ligger i prio ovan, men inte som en ren BOOL. Vill man ha det får man fråga

BOOL p= [prio boolValue]

Nedan är en figur som visar hur man kan sätta respektive hämta värdet av attributet messageText på dew två olika sätten.

Key-path

KVC innehåller också konceptet key-path som helt enkelt är en kedja av nycklar. "currentMessage.highPriority" frågar ett första objekt om det har en metod currentMessage som returnerar ett annat objekt som i sin tur får frågan om highPriority.

En finess som inte är direkt uppenbar är att det finns en massa snabel-a-nycklar som är en slags funktionsanrop, se Set and Array operators
Om en klass presenterar en tabell, kallad measurements av flyttalsvärden så kan man till exempel finna medelvärdet med

double average =[[obj valueForKeyPath:@"@avg.measurements"] doubleValue];

Eventuella nackdelar med KVC?

Som tidigare nämnt boxas alla icke-objekt in i NSNumber så att valueForKey: och setValue:forKey: bara behöver bry sig om returvärde/argument av typen id. Det förfular koden lite.
Men farten då… massa stränghantering måste sänka prestandan? Varje uppslag innebär att en sträng ska parsas, en matchande selektor ska finnas.

Det är sant, men jag hoppas att någon form av cachning av selektorer görs. Det borde gå i alla fall.

Men vad var fördelarna nu då med KVC?

  • strängar i stället för selektorer. Öppnar för nästa punkt:
  • kunna sätta ihop sökvägar genom ens data. Man kan säga att key-path är ett sätt att navigera runt i sin datamängd – att hoppa mellan objekt.

Puh. Det om det!

,

---
---