Wann sollte ich @synthesize explizit verwenden?

80

Soweit ich weiß, werden seit XCode 4.4 @synthesizedie Eigenschafts-Accessoren automatisch generiert. Aber gerade jetzt habe ich ein Codebeispiel darüber gelesen NSUndoManagerund im Code festgestellt, dass das @synthesizeexplizit hinzugefügt wird. Mögen:

@interface RootViewController  ()

@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@property (nonatomic, strong) NSUndoManager *undoManager;

@end

@implementation RootViewController
//Must explicitly synthesize this
@synthesize undoManager;

Ich bin jetzt verwirrt ... Wann sollte ich @synthesizemeinen Code explizit ergänzen ?

罗泽轩
quelle
1
Der Beispielcode ist möglicherweise alt. Verwenden Sie es grundsätzlich, es sei denn, es wird zu einem Problem (zum Beispiel werden Eigenschaften in Delegaten nicht automatisch synthetisiert)
borrrden
1
Versuchen Sie das auskommentieren @sythesize. Wenn der Code immer noch funktioniert, ist er nicht erforderlich.
ThomasW

Antworten:

170

Es gibt viele Antworten, aber auch große Verwirrung. Ich werde versuchen, etwas Ordnung zu schaffen (oder das Chaos zu erhöhen, wir werden sehen ...)

  1. Hören wir auf, über Xcode zu reden. Xcode ist eine IDE . clang ist ein Compiler . Diese Funktion, die wir diskutieren, wird als Autosynthese von Eigenschaften bezeichnet und ist eine Objective-C-Spracherweiterung, die von clang unterstützt wird , dem von Xcode verwendeten Standard-Compiler.
    Nur um es klar zu machen: Wenn Sie in Xcode zu gcc wechseln, profitieren Sie nicht von dieser Funktion (unabhängig von der Xcode-Version). Ebenso wie Sie, wenn Sie einen Texteditor verwenden und mit clang über die Befehlszeile kompilieren werden.

  2. Dank der Autosynthese müssen Sie die Eigenschaft nicht explizit synthetisieren, da sie vom Compiler automatisch als synthetisiert wird

    @synthesize propertyName = _propertyName
    

    Es gibt jedoch einige Ausnahmen:

    • readwrite-Eigenschaft mit benutzerdefiniertem Getter und Setter

      bei der Bereitstellung von sowohl eine Getter und Setter benutzerdefinierte Implementierung wird die Eigenschaft nicht automatisch synthetisiert werden

    • schreibgeschützte Eigenschaft mit benutzerdefiniertem Getter

      Wenn Sie eine benutzerdefinierte Getter-Implementierung für eine schreibgeschützte Eigenschaft bereitstellen, wird diese nicht automatisch synthetisiert

    • @dynamic

      Bei Verwendung @dynamic propertyNamewird die Eigenschaft nicht automatisch synthetisiert werden (ziemlich offensichtlich, da @dynamicund @synthesizesich gegenseitig aus)

    • Eigenschaften, die in einem @protocol deklariert sind

      Bei Übereinstimmung mit einem Protokoll wird eine vom Protokoll definierte Eigenschaft nicht automatisch synthetisiert

    • Eigenschaften, die in einer Kategorie deklariert sind

      Dies ist ein Fall, in dem die @synthesizeDirektive vom Compiler nicht automatisch eingefügt wird, diese Eigenschaften jedoch auch nicht manuell synthetisiert werden können. Während Kategorien Eigenschaften deklarieren können, können sie überhaupt nicht synthetisiert werden, da Kategorien keine Ivars erstellen können. Der Vollständigkeit halber möchte ich hinzufügen, dass es immer noch möglich ist , die Eigenschaftssynthese mithilfe der Objective-C-Laufzeit zu fälschen .

    • überschriebene Eigenschaften (neu seit clang-600.0.51, Versand mit Xcode 6, danke Marc Schlüpmann)

      Wenn Sie eine Eigenschaft einer Oberklasse überschreiben, müssen Sie sie explizit synthetisieren

Es ist erwähnenswert, dass beim Synthetisieren einer Eigenschaft das Backing-Ivar automatisch synthetisiert wird. Wenn also die Eigenschaftssynthese fehlt, fehlt auch das Ivar, sofern nicht ausdrücklich deklariert.

Mit Ausnahme der letzten drei Fälle besteht die allgemeine Philosophie darin, dass @dynamicder Compiler bei jeder manuellen Angabe aller Informationen zu einer Eigenschaft (durch Implementierung aller Zugriffsmethoden oder Verwendung ) davon ausgeht, dass Sie die vollständige Kontrolle über die Eigenschaft wünschen, und die Autosynthese deaktiviert es.

Abgesehen von den oben aufgeführten Fällen besteht die einzige andere Verwendung eines Expliziten @synthesizedarin, einen anderen Ivar-Namen anzugeben. Da Konventionen jedoch wichtig sind, empfehle ich, immer die Standardbenennung zu verwenden.

Gabriele Petronella
quelle
Wenn der ursprüngliche Fragesteller noch hier ist, sollte diese Antwort meiner Meinung nach akzeptiert werden. Es ist das vollständigste.
Steven Fisher
3
Soweit ich mich erinnere, werden in einer Kategorie angegebene Eigenschaften auch nicht automatisch synthetisiert. (Der zugrunde liegende Grund ist, dass Sie einer Kategorie keine Instanzvariable hinzufügen können.)
Martin R
@ MartinR, guter Punkt. Sie werden nicht automatisch synthetisiert, können aber auch nicht manuell synthetisiert werden, da @synthesizeeine Kategorie verboten ist (kein Ivar in Kategorien, wie Sie bereits bemerkt haben). Ich werde eine Notiz hinzufügen.
Gabriele Petronella
Diese Antwort geht jedoch nicht auf die spezifische Frage ein: WANN sollte @synthesize verwendet werden? Immer, nie, nur wenn bestimmte Bedingungen erfüllt sind?
Jeff
21

Wenn Sie @synthesizeden Compiler nicht explizit verwenden, wird Ihre Eigenschaft genauso verstanden, wenn Sie geschrieben haben

@synthesize undoManager=_undoManager;

dann können Sie in Ihren Code Dinge schreiben wie:

[_undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

Dies ist die übliche Konvention.

wenn du schreibst

@synthesize undoManager;

du wirst haben :

[undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

Persönlich höre ich auf zu benutzen @synthesize, da es nicht mehr obligatorisch ist. Für mich ist der einzige Grund zu verwenden @synthesize, ein iVarmit einem zu verknüpfen @property. Wenn Sie einen bestimmten Getter und Setter dafür generieren möchten. Aber in dem gegebenen Code gibt es keine iVar, ich denke, dass dies @synthesizenutzlos ist. Aber jetzt denke ich, die neue Frage ist "Wann verwenden iVar?", Und ich habe keine andere Antwort als "nie" für diese!

KIDdAe
quelle
2
Einverstanden, ich habe aufgehört zu verwenden, @synthesizeda es keinen Grund mehr gibt, es zu tun. Der führende Unterstrich dient auch als schönes visuelles Flag, um Sie darüber zu informieren, dass Sie sich mit dem Speicherverwaltungs- und Threading-Sicherheitsnetz befassen, das die Eigenschaften bieten (die Sie überspringen sollten, initund deallocMethoden).
BergQuester
1
Die Frage war, wann Sie explizit synthetisieren müssen. Das hast du überhaupt nicht beantwortet.
Fogmeister
1
Ich habe heute festgestellt, dass dies nicht ganz stimmt. Ohne @synthesize sind Sie für die Erstellung der Hintergrundinstanzvariablen verantwortlich. Damit erledigt der Compiler das für Sie.
Steven Fisher
1
Und ich habe niemanden für diese Entdeckung abgelehnt. :)
Steven Fisher
1
-1 Ihnen fehlen die Fälle, in denen Sie eine explizite Synthese benötigen. Auch dies ist eine Compiler-Funktion, keine Xcode-Funktion.
Gabriele Petronella
14

Wann sollte ich @synthesizemeinen Code explizit ergänzen ?

Im Allgemeinen, wenn es erforderlich ist: Sie werden wahrscheinlich nie einen Fall treffen, in dem es erforderlich ist.

Es gibt jedoch einen Fall, den Sie vielleicht nützlich finden.

Angenommen, Sie schreiben sowohl einen benutzerdefinierten Getter als auch einen Setter, möchten jedoch, dass eine Instanzvariable diesen unterstützt. (Für eine atomare Eigenschaft ist dies so einfach wie der Wunsch nach einem benutzerdefinierten Setter: Der Compiler schreibt einen Getter, wenn Sie einen Setter für eine einatomige Eigenschaft angeben, jedoch keine atomare Eigenschaft.)

Bedenken Sie:

@interface MyObject:NSObject
@property (copy) NSString *title;
@end

@implementation MyObject

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Dies wird nicht funktionieren, da _titlees nicht existiert. Sie haben sowohl einen Getter als auch einen Setter angegeben, sodass Xcode (korrekt) keine Hintergrundinstanzvariable dafür erstellt.

Geben Sie hier die Bildbeschreibung ein

Sie haben zwei Möglichkeiten, um es zu schaffen. Sie können entweder Folgendes ändern @implementation:

@implementation MyObject {
    NSString *_title;
}

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Oder ändern Sie es in Folgendes:

@implementation MyObject

@synthesize title = _title;

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Mit anderen Worten, obwohl Synthetisieren für praktische Zwecke niemals erforderlich ist *, kann es verwendet werden, um Instanzvariablen zur Sicherung von Eigenschaften zu definieren , wenn Sie einen Getter / Setter bereitstellen. Hier können Sie entscheiden, welches Formular Sie verwenden möchten.

In der Vergangenheit habe ich es vorgezogen, die Instanzvariable in der anzugeben @implementation {}, aber jetzt denke ich, dass die @synthesizeRoute eine bessere Wahl ist, da sie den redundanten Typ entfernt und die Hintergrundvariable explizit an die Eigenschaft bindet:

  1. Ändern Sie den Typ der Eigenschaft, und der Typ der Instanzvariablen ändert sich.
  2. Ändern Sie das Speicherqualifikationsmerkmal (z. B. machen Sie es schwach statt stark oder stark statt schwach), und das Speicherqualifikationsmerkmal ändert sich.
  3. Wenn Sie die Eigenschaft entfernen oder umbenennen, @synthesizewird ein Compilerfehler generiert. Sie werden nicht mit streunenden Instanzvariablen enden.

* -Ich kenne einen Fall, in dem es notwendig war, die Funktionalität auf Kategorien in mehrere Dateien aufzuteilen. Und ich wäre nicht überrascht, wenn Apple dies behebt oder sogar schon behebt.

Steven Fisher
quelle
Bist du sicher? Hast du es überhaupt versucht? Ich habe viele benutzerdefinierte Setter und Getter geschrieben und NIEMALS meine Eigenschaften synthetisiert. (Und ich habe Ihr erstes Beispiel verwendet, von dem Sie sagten, dass es nicht funktioniert)
Marc
Ja, ich bin mir sicher. Code wurde aus einem neuen Projekt kopiert. In neueren Versionen von Xcode funktioniert dies nur, wenn Sie in der Eigenschaft nichtatomar angeben.
Steven Fisher
1
Ja, das stimmt. Aber in 99% der Fälle sind Ihre Eigenschaften nichtatomar. Nur in dem seltenen Fall, dass Ihre Eigenschaft atomar ist und Sie einen benutzerdefinierten Getter / Setter möchten, den Sie tatsächlich synthetisieren müssen.
Marc
Wenn Sie eine benutzerdefinierte Implementierung bieten sowohl Setter und Getter, dann wird der Compiler davon ausgehen , dass Sie die Kontrolle über die Immobilie nehmen und es wird es nicht für Sie synthetisieren.
Gabriele Petronella
2
Da stimme ich Ihnen zu, obwohl ich verstehen kann, warum Apple dies ursprünglich für eine gute Idee hielt. Der eigentliche Schmerz ist, dass er atomicerst später als Eigenschaftsspezifizierer hinzugefügt wurde. Jetzt, wo es da ist, könnte die Warnflagge CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESinteressant sein.
Steven Fisher
7

OK, wenn Sie eine Eigenschaft erstellen ...

@property NSString *name;

Xcode synthetisiert automatisch einen iVar, als hätten Sie geschrieben ...

@synthesize name = _name;

Dies bedeutet, dass Sie mit ...

self.name;
// or
_name;

Beides funktioniert, verwendet jedoch nur self.namedie Accessor-Methoden.

Es gibt nur ein Mal, dass die automatische Synthese nicht funktioniert: Wenn Sie nur den Setter UND die Getter-Methode überschreiben, müssen Sie den iVar synthetisieren.

Sie sind in Ordnung, wenn Sie nur den Setter überschreiben oder wenn Sie nur den Getter überschreiben. Wenn Sie jedoch beides tun, wird der Compiler es nicht verstehen und Sie müssen es manuell synthetisieren.

Als Faustregel gilt jedoch.

Mach keine iVars. Nutzen Sie einfach die Eigenschaft. Synthetisiere es nicht.

Fogmeister
quelle
1
Es gibt weitere Fälle, in denen die Autosynthese nicht durchgeführt wird. Schau dir meine Antwort an.
Gabriele Petronella
@GabrielePetronella können Sie diese Fälle für den durchschnittlichen Leser kurz und bündig auflisten?
Dan Rosenstark
@DanRosenstark das ist für jeden, der jetzt programmiert, irrelevant. Sie sollten wirklich Swift verwenden. Und TBH Ich denke, die ganze Synthesesache ist nicht einmal mehr eine Sache in ObjC. Es sei denn, Sie arbeiten an einer Codebasis, die mindestens 5 Jahre alt ist.
Fogmeister
@Fogmeister ja, es ist immer noch eine Sache für den Fall, dass Sie in Ihrer Antwort erwähnen (wo Sie sowohl den Setter als auch den Getter überschreiben). Und damit Objective-C nicht "für irgendjemanden relevant ist, der gerade programmiert", rufen Sie bitte meinen Tagesjob an und sagen Sie es ihnen. Informieren Sie auch Facebook, Google und Apple, die Objective-C intern mit großer Wirkung einsetzen.
Dan Rosenstark
Ich bin sicher, dass dies ohne Quora wahr ist, aber trotzdem: quora.com/…
Dan Rosenstark
1

Die Eigenschaftssynthese ist erforderlich, wenn eine Eigenschaft in einem Protokoll deklariert ist. Es wird nicht automatisch in einer Implementierungsschnittstelle synthetisiert.

Leo Natan
quelle
stimmt, aber es ist nur ein Fall. Vielleicht möchten Sie mehr ausarbeiten.
Gabriele Petronella
@ GabrielePetronella Es ist das einzige, an das ich in dieser späten Stunde denken kann. =]
Leo Natan
0

Vielen Dank für die Klarstellung. Ich hatte ein ähnliches Problem.

@synthesize firstAsset, secondAsset, audioAsset;
@synthesize activityView;

Nachdem ich sie auskommentiert hatte, ging ich durch und ersetzte zum Beispiel jedes Vorkommen durch

self.firstAsset Es scheint, dass ich auch firstAsset verwenden könnte, aber ich vermisse es, das " " zu oft zu sehen.

Harry McGovern
quelle
-1

Xcode erfordert keine explizite @synthesizeDeklaration.

Wenn Sie nicht schreiben, ist es @synthesizedasselbe wie:

@synthesize manager = _manager;

Der Beispielcode war möglicherweise alt. Sie werden es bald aktualisieren.

Sie können auf Ihre Eigenschaften zugreifen wie:

[self.manager function];

Dies ist die von Apple empfohlene Konvention. Ich folge ihm und empfehle Ihnen, dies auch zu tun!

Sam Fischer
quelle
2
Die Anweisung [_manager function]greift NICHT auf die Eigenschaft zu, sondern direkt auf das zugrunde liegende ivar .
CouchDeveloper
@CouchDeveloper Wenn Sie nicht picken wollen, sollten Sie präzise sein. Das Anwesen besteht aus ein paar Dingen, einschließlich des Ivar. Es ist also besser zu sagen, dass [_manager function]keine Accessoren der Eigenschaft verwendet werden.
Nikolai Ruhe
@CouchDeveloper - Danke dafür! Repariert! :)
Sam Fischer
1
@ NikolaiRuhe Du hast recht Nikolai, ich hätte genauer sein sollen. ;)
CouchDeveloper