2009-04-05 18 views
33

Ich versuche zu verstehen, wie Strategien einige Leute verwenden, um Instanz vars vs. Eigenschaften zu unterscheiden. Ein übliches Muster ist die folgende:Eigenschaft vs Instanzvariable

@interface MyClass : NSObject { 
    NSString *_myVar; 
} 

@property (nonatomic, retain) NSString *myVar; 
@end 

@implementation MyClass 
@synthesize myVar = _myVar; 

Nun, dachte ich die ganze Prämisse hinter dieser Strategie ist, so dass man leicht den Unterschied zwischen einem Ivar und Eigenschaft unterscheiden. Also, wenn ich die Speicherverwaltung von einer synthetisierten Eigenschaft geerbt verwenden möchten, würde ich etwas verwenden, wie zum Beispiel:

myVar = @"Foo"; 

Der andere Weg, es über selbstbeziehende würde [Ivar/Immobilien hier]..

Das Problem bei der Verwendung der @synthesize myVar = _myVar Strategie, die ich dachte, dass das Schreiben von Code wie:

myVar = some_other_object; // doesn't work. 

Der Compiler beschwert sich, dass nicht angemeldete myVar ist. Warum ist das der Fall?

Danke.

Antwort

27

Eigenschaften sind nur Setter und Getters für ivars und sollten (fast) immer anstelle von direktem Zugriff verwendet werden.

@interface APerson : NSObject { 
    // NSString *_name;   // necessary for legacy runtime 
} 

@property(readwrite) NSString *name; 

@end 

@implementation APerson 
@synthesize name;     // use name = _name for legacy runtime 
@end 

@synthesize schafft in diesem Fall die beiden Methoden (nicht 100% genau):

- (NSString *)name { 
    return [[_name copy] autorelease]; 
} 
- (void)setName:(NSString *)value { 
    [value retain]; 
    [_name release]; 
    _name = value; 
} 

Es ist jetzt einfach zwischen ivars und Getter/Setter zu unterscheiden. Die Accessoren haben das Präfix self.. Sie sollten auf die Variablen nicht direkt zugreifen.


Ihr Beispielcode nicht wie es sein sollte funktioniert:

_myVar = some_other_object;  // _myVar is the ivar, not myVar. 
self.myVar = some_other_object; // works too, uses the accessors 
+2

Nicht einen alten Beitrag wiederbeleben, aber ich habe mich über dieses Thema gefragt, als ich diese Frage sah. Dies scheint eine schöne Erklärung zu sein, aber für eine Situation, die mich getroffen hat. Wenn ich etwas wie '[someObject.someArray addObject: anotherObject]' tue, würde das nicht bedeuten, dass das Objekt 'anotherObject' zu einer temporären Kopie des Arrays' someArray' hinzugefügt wird und die tatsächliche Eigenschaft nicht beeinflusst? –

+0

@Rickay: Das Standardverhalten ist das Kopieren, sodass ein externes Objekt den internen Status nicht ändern kann. Sie können jedoch 'copy' in' retain' ändern, wenn Sie ein veränderbares Array anbieten möchten. –

+0

Ahh, ich verstehe, danke, dass du das geklärt hast. –

8

A synthetisiert Eigenschaft namens prop dargestellt tatsächlich durch zwei Methoden prop (den aktuellen Wert der Eigenschaft zurückkehrt) und setProp: (einen neuen Wert für prop setzen).

Die self.prop Syntax ist syntaktischer Zucker zum Aufruf eines dieser Accessoren. In Ihrem Beispiel können Sie alle eine der folgenden Aktionen die Eigenschaft festlegen myVar:

self.myVar = @"foo"; // handles retain/release as specified by your property declaration 
[self setMyVar: @"foo"]; // handle retain/release 
_myVar = @"Foo"; // does not release old object and does not retain the new object 

Um die Eigenschaften zuzugreifen, verwenden self.propname. Um auf Instanzvariablen zuzugreifen, verwenden Sie nur den Namen der Instanzvariablen.

2

Im Allgemeinen benenne ich meine Eigenschaften genauso wie meine Instanzvariablen; Dies ist die Standardannahme, die die @property Syntax macht. Wenn Sie feststellen, dass Sie mit den Standardeinstellungen kämpfen, tun Sie es falsch (oder Ihre Framework-SUX, was für Cocoa/Cocoa-Touch meiner Meinung nach nicht der Fall ist).

Der Compiler Fehler Sie bekommen ist, weil Eigenschaft Gebrauch immer einen Objektverweis haben hat, auch in Ihrer eigenen Klassenimplementierung:

self.stuff = @"foo"; // property setter 
[stuff release]; // instance variable 
stuff = @"bar"; // instance variable 
return self.stuff; // property getter 

Ich weiß, dass viele Cocoa-Programmierer nicht einverstanden ist, aber ich denke, es ist schlecht üben Sie, Eigenschaften innerhalb Ihrer Klassenimplementierung zu verwenden.Ich würde lieber etwas sehen:

-(void) someActionWithStuff: (NSString*) theStuff { 
    // do something 
    [stuff release]; 
    stuff = [theStuff copy]; 
    // do something else 
} 

als dies:

-(void) someActionWithStuff: (NSString*) theStuff { 
    // do something 
    self.stuff = theStuff; 
    // do something else 
} 

Ich ziehe Speicherverwaltung so explizit wie möglich zu tun. Aber selbst wenn Sie nicht zustimmen, wird die Verwendung des Formulars self.stuff in jedem erfahrenen Objective-C-Programmierer darauf hinweisen, dass Sie eine Eigenschaft aufrufen und nicht auf eine Instanzvariable zugreifen. Es ist ein subtiler Punkt, der für Anfänger leicht zu beschönigen ist, aber nachdem Sie eine Weile mit Objective-C 2.0 gearbeitet haben, ist es ziemlich klar.

+0

Das Problem damit, wie Sie es beschreiben, ist, dass Sie sich bei der Duplizierung des Speicherverwaltungscodes, wie Sie es gezeigt haben, offen für Fehler machen (zum Beispiel stürzt Ihre angepasste Version von someActionWithStuff ab, wenn Sie [selfActionWithStuff: stuff] machen!) . Wenn Sie das Verhalten von setStuff ändern, fragen Sie sich außerdem, wo Sie die Änderung möglicherweise noch vornehmen müssen. –

+4

"... es ist eine schlechte Übung, Eigenschaften in Ihrer Klassenimplementierung zu verwenden." Nein, ist es nicht. Die Verwendung der Zugriffsmethoden (unabhängig davon, ob es sich um die Syntax des Eigenschaftszugriffs oder explizit handelt) verursacht KVO-Benachrichtigungen kostenlos. Wenn Sie die Instanzvariable direkt eingeben, müssen Sie Ihre eigenen KVO-Benachrichtigungen veröffentlichen, die Sie * an mindestens einem Ort vergessen werden. –

+0

Wenn Accessoren innerhalb der Klassenimplementierung verwendet werden, bedeutet das, dass Sie @property-Zugriffsmethoden für _every_ ivar haben? Auf der einen Seite klingt dies z.B. zum Lazy-Laden von etwas in Accessoren oder der automatischen Retain/Release-Verwaltung von @synthesize. Aber würde das nicht bedeuten, dass private Ivars, die interne Zustände enthalten (wie etwa Lock- und Condition-Objekte), anderen Klassen ausgesetzt sind? (Zugriffsmethoden sind Methoden und soweit ich mich erinnere, sind Methoden in Objective-C immer öffentlich) – Zargony

0

Don,

Nach den "Regeln", sollten Sie Veröffentlichung für jede Kopie, Alloc aufrufen und aufbewahren. Also, warum rufst du Release auf? Geht das davon aus, dass es mit Alloc, Copy oder Retain erstellt wurde?

Das wirft eine andere Frage auf: Ist es schädlich, Release auf einen Verweis auf ein Objekt zu nennen, wenn es bereits freigegeben wurde?

+0

Don gibt die ivar-Sachen frei, weil eine Kopie erstellt wird, wenn sie zugewiesen ist (siehe folgende Zeile). Dies ist (mehr oder weniger) der gleiche Wert wie der @ synthesized-Eigenschaften-Setter, wenn er mit dem Attribut copy (oder ähnlich beibehalten) verwendet wird. Und ja, es ist absolut schädlich, die Freigabe für eine Referenz aufzurufen, nachdem Sie die Freigabe aufgerufen haben, es sei denn, Sie haben zwei Eigentumsrechte an dem Objekt, das freigegeben werden soll. Siehe die Speicherverwaltungsregeln unter . –

+0

Also, die Freigabe eines Referenzzeigers beinhaltet mehr als nur das sofortige Alloc, Copy und Retain? Stattdessen sollten Referenzen vor einer neuen Referenzzuweisung freigegeben werden (es sei denn, die Eigenrequisite wird verwendet, die dies unter den Abdeckungen ausführt)? Das scheint falsch. Aber bitte bestätigen Sie, wie ich mehr davon kaue. Vielen Dank! –

+0

Nach ein wenig mentaler Visualisierung macht dies Sinn. Die Referenzzählung muss manuell verwaltet werden, daher die Freigabe, bevor ihr eine neue Referenz zugewiesen wird. –

0

Da Apple das _ Präfix für sich selbst reserviert, und da ich es lieber mache, wenn ich den Setzer benutze und wenn ich den ivar benutze, habe ich praktischerweise ein Präfix von i_ auf meinen ivars verwendet , so zum Beispiel:

@interface MyClass : NSObject { 
    NSString *i_myVar; 
} 

@property (nonatomic, retain) NSString *myVar; 

@synthesize myVar = i_myVar; 

i_myVar = [input retain]; 
self.myVar = anotherInput; 
[i_myVar release] 

Da es sehr wichtig ist zu wissen, wenn Sie die Setter verwenden, und wenn Sie die ivar verwenden, finde ich das explizit anderen Namen sicherer ist.

In Ihrer Frage, sollte es sein:

self.myVar = @"Foo"; // with setter, equivalent to [self setMyVar:@"Foo"] 

und

_myVar = some_other_object; // direct ivar access - no memory management! 

Denken Sie daran, dass Sie nicht Setter/Getter in init/dealloc verwenden sollten, so können Sie Ihre direkten Ivar Zugang tun müssen, (und sorgfältige Speicherverwaltung) in diesen Methoden.

5

Das Problem bei der Verwendung der @synthesize myVar = _myVar Strategie, die ich dachte, dass das Schreiben von Code wie:

myVar = some_other_object; // doesn't work. 

Der Compiler beschwert sich, dass nicht angemeldete myVar ist. Warum ist das der Fall?

Weil die Variable myVar nicht deklariert ist.

Diese Anweisung verwendet die Syntax für den Zugriff auf eine Variable, sei es eine Instanzvariable oder eine andere Art. Wie rincewind Ihnen sagte, müssen Sie entweder die Eigenschaft Zugriffssyntax (self.myVar = someOtherObject) oder eine explizite Nachricht an die Accessor-Methode ([self setMyVar:someOtherObject]) zugreifen.

Andernfalls versuchen Sie, auf eine Variable zuzugreifen, und da Sie keine Variable mit dem Namen myVar haben, versuchen Sie, auf eine Variable zuzugreifen, die nicht existiert.

0

was einfach nicht stimmt mit

mit
@interface MyClass : NSObject 
@property NSString *prop; 
@end 

nonatomic und retain nicht erforderlich sind, halten, ist die Standardeinstellung, und atomare/nonatomic isn \ t wichtig es sei denn, XCode Sie mit einer Warnung erzählt.

es nicht notwendig ist, um die iVar zu erklären, wird man erstellt für Sie _prop genannt, wenn Sie wirklich ein verwenden wollen (ich sehe nicht, warum ehrlich zu sein)

@synthesize ist nicht erforderlich.

Wenn (und Sie sollten) mit ARC müssen Sie nicht mit Retain und Release entweder zu belästigen.

halten Sie es einfach!

weiterhin, wenn Sie ein Verfahren wie dieses

- (void)aMethod:(NSString*)string 
{ 
    self.prop = string; 
    // shows very clearly that we are setting the property of our object 

    _aName = string; 
    // what is _aName ? the _ is a convention, not a real visual help 
} 

haben würde ich immer Eigenschaften verwenden, flexibler, leichter zu lesen.

Verwandte Themen