12 Egenskaper (provavsnitt) 12.1 Egenskaper 12.2 Deklaration av egenskaper 12.3 Åtkomsttjänster för egenskaper 12.4 Åtkomsttjänster med genererade instansvariabler 12.5 Åtkomsttjänster med egna instansvariabelnamn 12.6 Åtkomsttjänster utan @synthesize-sats 12.7 Åtkomsttjänster med explicit implementation 12.8 Använda egenskaper via punktnotation 12.9 Egenskaper med endast läsrättighet
12.1 Egenskaper Objective-C har särskilt stöd för att arbeta med egenskaper: Deklareras med det reserverade ordet @property Klassen har åtkomsttjänster för egenskapens värde Åtkomsttjänsterna kan ge läs- eller läs- och skrivrättigheter för värdet Åtkomsttjänsterna behöver inte deklareras explicit i klassdefinitionen Metoder för åtkomsttjänsterna kan genereras automatiskt, om så önskas Vi kan skräddarsy hur egenskaper som är pekare till objekt ska hanteras Egenskaper kan kombineras med olika tekniker för minneshantering Egenskaper hos objekt blir åtkomliga via en förenklande punktnotation Stödet för egenskaper infördes i Objective-C 2.0. Precis som flera andra objektorienterade programspråk har Objective-C fr o m version 2.0 särskilt stöd i språket för att arbeta med egenskaper. En egenskap (den engelska termen är property) hos ett objekt är något som har ett värde som vi lätt vill kunna hantera, t ex via åtkomsttjänster i klassen. Egenskaper hos en klass i Objective-C kan deklareras med det reserverade ordet @property. Det är en sådan deklaration som öppnar vägen för de fördelar som beskrivs i återstoden av kapitlet, t ex kodgenerering av åtkomsttjänster och en ny syntax för åtkomst. När en egenskap deklareras i en klass innebär det ett löfte om att klassen ska tillhandahålla åtkomsttjänster för egenskapen. Vi behöver inte längre deklarera åtkomsttjänsterna explicit. En egenskap kan deklareras med antingen endast läsrättigheter för dess värde (dvs det finns endast en läsande åtkomsttjänst), eller både läs- och skrivrättigheter för dess värde (dvs det finns både en läsande och en ändrande åtkomsttjänst). Koden för metoderna för åtkomsttjänsterna för en egenskap kan genereras automatiskt. Alternativt är det fortfarande möjligt att implementera åtkomstmetoderna för hand. Vi kan påverka vilken kod som genereras för en åtkomsttjänst. Bl a kan vi skräddarsy hur egenskaper som är pekare till objekt ska hanteras (delad tillgång till objekt eller egna unika kopior av objekt). Det finns särskilt stöd för att hantera egenskaper med olika tekniker för minneshantering, så att rätt kod genereras i förhållande till den minneshantering som används. Egenskaper hos ett objekt blir automatiskt åtkomliga via en punktnotation som påminner om den för fält i poster, men där det fortfarande är egenskapens åtkomsttjänster som används. Sammantaget kan detta alltså betyda att vi slipper såväl deklarera, implementera som anropa åtkomsttjänster explicit!
12.2 Programmera i Objective-C/1.0 Deklaration av egenskaper Time24.h @interface Time : NSObject @property int minutes; @property int seconds; + (id) timewithhours: (int) h minutes: (int) m seconds: (int) s; - (id) initwithhours: (int) h minutes: (int) m seconds: (int) s; Egenskaper måste deklareras med typ och namn Egenskaper får deklareras i: klassdefinitioner gränssnittsdefinitioner klassutbyggnad Om vi betraktar klassen Time från föregående kapitel, finns det tre tydliga egenskaper hos varje objekt av klassen: timtalet, minuttalet och sekundtalet. Vi deklarerar en egenskap i en klassdefinition genom att efter klassnamnet och arvsrelationen lägga till en eller flera egenskapsdeklarationer. Varje egenskapsdeklaration består av det reserverade ordet @property, egenskapens typ, egenskapens namn samt semikolon: Mellan det reserverade ordet @property och egenskapens typ kan det förekomma ett eller flera kommauppräknade attribut inom parenteser. Vi återkommer till dessa attribut senare i kapitlet: @property(readonly) int hours; Varje egenskapsdeklaration är ett löfte om att det kommer att finnas åtkomsttjänster för var och en av de deklarerade egenskaperna. Om inget annat sägs ska egenskapen vara både läs- och skrivbar, dvs ha en läsande åtkomsttjänst med samma selektor som egenskapens namn, inga argument och egenskapens typ som returtyp samt en ändrande åtkomsttjänst med en selektor som består av prefixet set, egenskapens namn med stor begynnelsebokstav samt kolon, ett argument av egenskapens typ och returtypen void. En deklaration av egenskapen hours motsvarar alltså att vi hade deklarerat följande tjänster i klassen: - (int) hours; - (void) sethours: (int) h; Observera att klassen Time inte längre deklarerar några åtkomsttjänster explicit i klassdefinitionen.
12.3 Åtkomsttjänster för egenskaper #import "Time24.h" @implementation Time { int hours; int minutes; int seconds; Time24.m @synthesize hours, minutes, seconds; + (id) timewithhours: (int) h minutes: (int) m seconds: (int) s { return [[self alloc] initwithhours: h minutes: m seconds: s]; - (id) initwithhours: (int) h minutes: (int) m seconds: (int) s { self = [super init]; if (self!= nil) { hours = h; minutes = m; seconds = s; return self; - (NSString *) description { return [NSString stringwithformat: @"%02d:%02d:%02d", [self hours], [self minutes], [self seconds]]; Här visas hela klassimplementationen för klassen Time. För de egenskaper som deklarerats i klassdefinitionen (dvs i vårt fall egenskaperna hours, minutes och seconds) kan vi få kompilatorns hjälp att generera metoder för egenskapernas åtkomsttjänster. Det gör vi genom att någonstans mellan de reserverade orden @implementation och lägga till en @synthesize-sats, bestående av det reserverade ordet @synthesize följt av en kommauppräknad lista av egenskaper som det ska genereras åtkomsttjänster för. Satsen avslutas med semikolon: @synthesize hours, minutes, seconds; Vi hade också kunnat använda tre @synthesize-satser, en per egenskap. För egenskapen hours innebär detta exempelvis att det genereras två metoder motsvarande: - (int) hours { return hours; - (void) sethours: (int) h { hours = h; Som synes förutsätter dessa metoder att det finns en instansvariabel med samma namn som egenskapen. Vi har därför ändrat namnen på instansvariablerna i deklarationerna längst upp i klassimplementationen. Vi är inte tvungna att använda @synthesize-satser för att generera åtkomstmetoder för en egenskap. Det är fullt tillåtet att implementera åtkomstmetoderna för hand.
12.4 Programmera i Objective-C/1.0 Åtkomsttjänster med genererade instansvariabler @synthesize-satsen kan även generera instansvariabler för egenskaper: Time24.h @interface Time : NSObject @property int minutes; @property int seconds; + (id) timewithhours: (int) h minutes: (int) m seconds: (int) s; - (id) initwithhours: (int) h minutes: (int) m seconds: (int) s; #import "Time24.h" @implementation Time Time24.m @synthesize hours, minutes, seconds;... Endast om målplattformen är Mac OS X 10.5 (64 bitar) eller senare, eller ios I exemplet på föregående sida deklarerade vi instansvariabler som matchade namnen på egenskaperna som därefter @synthesize-satsen genererade åtkomstmetoder för. Vi hade också, som bilden på denna sida visar, kunnat utelämna deklarationerna av instansvariablerna i klassdefinitionen och i stället låta @synthesize-satsen generera deklarationer av instansvariablerna åt oss. Instansvariablernas ges då automatiskt samma namn som motsvarande egenskap. Dessa instansvariabler är fortfarande fullt åtkomliga i klassens metoder. Detta fungerar emellertid endast om vi ska generera kod för de målplattformar som har en tillräckligt modern runtimemiljö för Objective-C, vilket innebär att målplattformen måste vara en 64 bitars version av Mac OS X i version 10.5 eller senare, eller ios.
12.5 Åtkomsttjänster med egna instansvariabelnamn Time24.h @interface Time : NSObject @property int minutes; @property int seconds;... #import "Time24.h" @implementation Time { int myhours; int myminutes; int myseconds; Time24.m @synthesize hours = myhours; @synthesize minutes = myminutes; @synthesize seconds = myseconds;... Som vi såg tidigare i kapitlet kommer en @synthesize-sats normalt att generera kod som förutsätter att värdet av en egenskap ska lagras i en instansvariabel med samma namn. Om vi av något skäl vill att en instansvariabel ska ha ett annat namn, t ex om vi vill behålla de instansvariabler som vi deklarerade i föregående kapitel (myhours, myminutes och myseconds), kan vi ange instansvariabelns korrekta namn efter ett likhetstecken i @synthesize-satsen: @synthesize hours = myhours; @synthesize minutes = myminutes; @synthesize seconds = myseconds; Om vi vill lagra egenskapens värde på ett helt annat sätt än i en instansvariabel i det egna objektet, återstår möjligheten att implementera åtkomstmetoderna för hand. Se senare i kapitlet!
12.6 Programmera i Objective-C/1.0 Åtkomsttjänster utan @synthesize-sats @synthesize-satsen kan utelämnas helt fr o m Apple LLVM compiler 4.0: Time24.h @interface Time : NSObject @property int minutes; @property int seconds; + (id) timewithhours: (int) h minutes: (int) m seconds: (int) s; - (id) initwithhours: (int) h minutes: (int) m seconds: (int) s; #import "Time24.h" @implementation Time Time24.m... Metoder för åtkomsttjänsterna genereras automatiskt Instansvariablerna namnges med understreck följt av egenskapens namn From Apple LLVM compiler 4.0 (dvs fr o m Xcode 4.4) är det möjligt att gå ett steg längre och helt utelämna @synthesize-satsen. Detta leder automatiskt till att såväl metoder för egenskapernas åtkomsttjänster som de nödvändiga instansvariablerna för egenskaperna genereras. De instansvariabler som genereras på detta sätt ges emellertid namn efter en avvikande regel: namnen byggs upp av ett understreck följt av den motsvarande egenskapens namn. I bildens exempel ges instansvariablerna alltså namnen _hours, _minutes resp _seconds. Dessa instansvariabler är fortfarande fullt åtkomliga i klassens metoder, men tanken med de avvikande variabelnamnen är att inte uppmuntra till oavsiktlig direkt användning av instansvariablerna i stället för egenskapernas åtkomsttjänster. Observera att det med tidigare versioner av Apples kompilatorer hade en helt annat effekt att utelämna @synthesize-satsen för en egenskap: det innebar att man visserligen slapp deklarera åtkomsttjänster för hand i klassdefinitionen (eftersom @property-satsen innebar ett löfte om att de fanns), men man var tvungen att både deklarera instansvariabler och implementera metoder för åtkomsttjänsterna själv.
12.7 Åtkomsttjänster med explicit implementation Time24.h @interface Time : NSObject @property int minutes; @property int seconds; + (id) timewithhours: (int) h minutes: (int) m seconds: (int) s; - (id) initwithhours: (int) h minutes: (int) m seconds: (int) s; #import "Time24.h" @implementation Time Time24.m... - (void) sethours: (int) h { _hours = h % 24; Vi är inte bundna till att låta metoder för en egenskaps åtkomsttjänster genereras av kompilatorn. Det är fullt tillåtet att förse klassen med en explicit implementation av en eller båda åtkomstmetoderna för en egenskap. I bildens exempel genereras den läsande åtkomstmetoden hours av kompilatorn, medan vi definierar den ändrande åtkomstmetoden sethours: explicit: - (void) sethours: (int) h { _hours = h % 24; I metoden normaliserar vi timtalet till ett heltal i intervallet 0-23, så att klockslag som 24:00:00 omvandlas till en standardiserad form som alltid ger samma egenskapsvärden för samma klockslag på dygnet. Observera att vi inte behöver deklarera tjänsten sethours: explicit i klassdefinitionen. Vi har redan lovat att tjänsten finns i och med deklarationen av egenskapen hours: Så länge vi endast implementerar den ena åtkomsttjänsten för en läs- och skrivbar egenskap explicit, kommer Apple LLVM compiler 4.0 och senare versioner fortfarande att generera en instansvariabel för egenskapen automatiskt. Om vi däremot väljer att implementera samtliga åtkomsttjänster för en egenskap explicit, måste vi själva se till att skapa en instansvariabel för egenskapen. Ett sätt är att använda en explicit @synthesize-sats: @synthesize hours = _hours;
12.8 Programmera i Objective-C/1.0 Använda egenskaper via punktnotation Egenskaper är åtkomliga via notationen objektpekare.egenskapsnamn Kan användas såväl för att läsa som att ändra egenskapen Utnyttjar klassens åtkomsttjänster inte instansvariablerna! Källkoden: Time *endtime = [Time timewithhours: 12 minutes: 30 seconds: 0]; int endhour = endtime.hours; // 12 endtime.minutes = 45; // 12:45:00 endtime.hours += 3; // 15:45:00 kommer alltså att tolkas som: Time *endtime = [Time timewithhours: 12 minutes: 30 seconds: 0]; int endhour = [endtime hours]; // 12 [endtime setminutes: 45]; // 12:45:00 [endtime sethours: [endtime hours] + 3]; // 15:45:00 Vi har redan sett att egenskapsdeklarationer gör att vi slipper deklarera och implementera åtkomsttjänster. Det finns emellertid ytterligare en fördel som vi kan dra nytta av, om vi vill: vi slipper t o m anropa dem. I och med att vi deklarerar en egenskap hos en klass av objekt, får vi tillgång till en förenklad syntax för att läsa och ändra egenskapens värde. Syntaxen bygger på en punktnotation (lik den för fält i poster) där egenskaper är åtkomliga via uttryck av formen objektpekare.egenskapsnamn. Vi kan använda denna syntax för att hämta värdet av en egenskap, så att uttrycket endtime.hours tolkas som meddelandet [endtime hours]. På samma sätt kan vi ändra värdet av en egenskap med den nya syntaxen: endtime.minutes = 45; vilket har samma effekt som meddelandet: [endtime setminutes: 45]; Detta fungerar även med sammansatta tilldelningsoperatorer, t ex: endtime.hours += 3; som får samma effekt som: [endtime sethours: [endtime hours] + 3]; Observera att vi fortfarande använder klassens åtkomsttjänster vi manipulerar inte instansvariablerna direkt!
12.9 Egenskaper med endast läsrättighet Egenskaper deklarerade med attributet readonly ges endast läsrättighet: Account.h @interface Account : NSObject @property(readonly) NSString *accountno; @property(readonly) long balance; + (id) accountwithaccountno: (NSString *) no; + (id) accountwithaccountno: (NSString *) no balance: (long) amount; - (id) initwithaccountno: (NSString *) no; - (id) initwithaccountno: (NSString *) no balance: (long) amount; - (long) interest; - (void) deposit: (long) amount; - (void) withdraw: (long) amount; - (void) transfer: (long) amount to: (Account *) toaccount; - (void) printdetails; Endast metoden för den läsande åtkomsttjänsten genereras En egenskapsdeklaration gäller normalt en egenskap med både läs- och skrivrättigheter, vilket innebär ett löfte om att det finns såväl en läsande som en ändrande åtkomsttjänst. För en sådan egenskap kommer automatiskt metoder för båda åtkomsttjänsterna att genereras (såvida de inte implementerats explicit). Det är möjligt att deklarera en egenskap med enbart läsrättigheter, dvs en egenskap där löftet inskränker sig till att det ska finnas en läsande åtkomsttjänst. Detta görs genom att ange attributet readonly inom parenteser efter det reserverade ordet @property i egenskapsdeklarationen: @property(readonly) NSString *accountno; För en sådan egenskap skulle följdriktigt endast en metod för den läsande åtkomsttjänsten (accountno) genereras. I klassen Account från tidigare kapitel skulle detta kunna passa för såväl egenskapen accountno (kontonummer) som balance (saldo), eftersom ett kontonummer aldrig bör ändras hos ett befintligt kontoobjekt, och saldot endast ändras relativt av tjänster som deposit: och withdraw:. En egenskap som inte uttryckligen deklareras med enbart läsrättigheter har alltid både läs- och skrivrättigheter. Detta kan anges explicit med attributet readwrite: @property(readwrite) int hours;