web-dev-qa-db-fra.com

Quand devrais-je utiliser @synthesize explicitement?

Pour autant que je sache, depuis XCode 4.4 le @synthesize générera automatiquement les accesseurs de la propriété. Mais tout à l'heure, j'ai lu un exemple de code sur NSUndoManager, et dans le code, il a remarqué que le @synthesize est ajouté explicitement. Comme:

@interface RootViewController  ()

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

@end

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

Je me sens perplexe maintenant ... Quand devrais-je ajouter @synthesize explicitement à mon code?

68
罗泽轩

Il y a beaucoup de réponses, mais aussi une grande confusion. Je vais essayer de mettre un peu d'ordre (ou d'augmenter les dégâts, on verra ...)

  1. Arrêtons de parler de Xcode. Xcode est un [~ # ~] ide [~ # ~] . Clang est un compilateur . Cette fonctionnalité dont nous discutons est appelée autosynthèse de propriétés et il s’agit d’un extension du langage Objective-C supportée par clang , qui est le compilateur par défaut utilisé par Xcode.
    Pour que ce soit bien clair, si vous passez à gcc dans Xcode, vous ne bénéficierez pas de cette fonctionnalité (quelle que soit la version de Xcode.) De la même manière si vous utilisez un éditeur de texte et compilez en utilisant clang la ligne de commande, vous voulez.

  2. Grâce à l’autosynthèse, vous n’avez pas besoin de synthétiser explicitement la propriété car elle sera automatiquement synthétisée par le compilateur.

    @synthesize propertyName = _propertyName
    

    Cependant, il existe quelques exceptions:

    • propriété readwrite avec getter et setter personnalisés

      lorsque vous fournissez à la fois une implémentation personnalisée de getter et de setter, la propriété ne sera pas automatiquement synthétisée

    • propriété readonly avec getter personnalisé

      lors de la fourniture d'une implémentation getter personnalisée pour une propriété en lecture seule, celle-ci ne sera pas automatiquement synthétisée

    • @ dynamic

      en utilisant @dynamic propertyName, la propriété ne sera pas automatiquement synthétisée (assez évident, puisque @dynamic et @synthesize s'excluent mutuellement)

    • propriétés déclarées dans un @protocol

      lors de la conformité à un protocole, aucune propriété définie par le protocole ne sera automatiquement synthétisée

    • propriétés déclarées dans une catégorie

      c'est un cas dans lequel la directive @synthesize n'est pas insérée automatiquement par le compilateur, mais ces propriétés ne peuvent pas non plus être synthétisées manuellement. Bien que les catégories puissent déclarer des propriétés, elles ne peuvent pas du tout être synthétisées, car les catégories ne peuvent pas créer d'ivars. Par souci d'exhaustivité, j'ajouterai que c'est toujours possible simuler la synthèse de la propriété à l'aide du moteur d'exécution Objective-C .

    • propriétés remplacées (nouveau depuis clang-600.0.51, livré avec Xcode 6, merci Marc Schlüpmann)

      lorsque vous substituez une propriété d'une super-classe, vous devez explicitement la synthétiser

Il est à noter que la synthèse d'une propriété synthétise automatiquement l'ivar de sauvegarde. Par conséquent, si la synthèse de la propriété est manquante, l'ivar sera également manquant, à moins d'être explicitement déclaré.

À l’exception des trois derniers cas, la philosophie générale est que chaque fois que vous spécifiez manuellement toutes les informations sur une propriété (en implémentant toutes les méthodes d’accesseur ou en utilisant @dynamic), Le compilateur supposera que vous souhaitiez un contrôle total sur la propriété et cela désactivera l'autosynthèse.

Hormis les cas énumérés ci-dessus, la seule autre utilisation d'un @synthesize Explicite serait de spécifier un nom d'ivar différent. Cependant, les conventions sont importantes, je vous conseille donc de toujours utiliser le nom par défaut.

155

Si vous n'utilisez pas explicitement @synthesize le compilateur comprendra votre propriété de la même manière si vous aviez écrit

@synthesize undoManager=_undoManager;

alors vous pourrez écrire dans votre code des choses comme:

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

C'est la convention commune.

si vous écrivez

@synthesize undoManager;

tu vas avoir :

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

Personnellement j'arrête d'utiliser @synthesize, puisque ce n'est plus obligatoire. Pour moi, la seule raison d'utiliser @synthesize est de lier un iVar à un @property. Si vous voulez générer un getter et un setter spécifiques. Mais dans le morceau de code donné il n'y a pas de iVar, je pense que cela @synthesize est inutile. Mais maintenant, je pense que la nouvelle question est "Quand utiliser iVar?", Et je n'ai pas d'autre réponse que "jamais" pour celle-ci!

17
KIDdAe

Quand devrais-je ajouter @synthesize explicitement à mon code?

Généralement, si cela est nécessaire: vous ne rencontrerez probablement jamais un cas où cela est nécessaire.

Il y a toutefois un cas qui pourrait vous être utile.

Supposons que vous écrivez à la fois un getter et un setter personnalisés, mais que vous souhaitiez qu’une variable d’instance les sauvegarde. (Pour une propriété atomique, cela est aussi simple que de vouloir un séparateur personnalisé: le compilateur écrira un getter si vous spécifiez un séparateur pour une propriété monatomique, mais pas pour une propriété atomique.)

Considère ceci:

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

@implementation MyObject

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

@end

Cela ne fonctionnera pas, car _title n'existe pas. Vous avez spécifié à la fois un getter ou un setter, donc Xcode (correctement) ne crée pas de variable d'instance de support pour celle-ci.

enter image description here

Vous avez deux choix pour le faire exister. Vous pouvez soit changer le @implementation pour ça:

@implementation MyObject {
    NSString *_title;
}

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

@end

Ou changez ceci en ceci:

@implementation MyObject

@synthesize title = _title;

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

@end

En d'autres termes, bien que la synthèse ne soit jamais nécessaire * à des fins pratiques, elle peut être utilisée pour définir des variables d'instance de sauvegarde de propriété lorsque vous fournissez un getter/setter. Vous pouvez choisir le formulaire que vous souhaitez utiliser.

Dans le passé, j’ai préconisé de spécifier la variable d’instance dans le fichier @implementation {}, mais je pense maintenant que le @synthesizeroute est un meilleur choix car il supprime le type redondant et lie explicitement la variable de support à la propriété:

  1. Modifiez le type de la propriété et le type de la variable d'instance change.
  2. Modifiez son qualificateur de stockage (par exemple, rendez-le faible au lieu de fort ou fort au lieu de faible) et le qualificateur de stockage change.
  3. Supprimez ou renommez la propriété, et le @synthesize générera une erreur de compilation. Vous ne vous retrouverez pas avec des variables d'instance parasites.

* -Je connais un cas où cela était nécessaire, en ce qui concerne le fractionnement des fonctionnalités entre catégories dans plusieurs fichiers. Et je ne serais pas surpris si Apple corrige cela, ou même le fait déjà.

11
Steven Fisher

OK, quand vous créez une propriété ...

@property NSString *name;

Xcode synthétisera automatiquement une iVar comme si vous aviez écrit ...

@synthesize name = _name;

Cela signifie que vous pouvez accéder à la propriété avec ...

self.name;
// or
_name;

Cela fonctionnera mais seulement self.name utilise réellement les méthodes d'accès.

Une seule fois, la synthèse automatique ne fonctionne pas: Si vous écrasez mais la méthode de définition ET la méthode de lecture, vous devrez synthétiser l'iVar.

Vous allez bien si vous remplacez simplement le passeur ou si vous remplacez simplement le getter. Mais si vous faites les deux, le compilateur ne le comprendra pas et vous devrez le synthétiser manuellement.

En règle générale cependant.

Ne faites pas iVars. Il suffit d'utiliser la propriété. Ne le synthétisez pas.

6
Fogmeister

La synthèse de propriété est requise lorsqu'une propriété est déclarée dans un protocole. Il ne sera pas automatiquement synthétisé dans une interface d'implémentation.

1
Leo Natan

Merci d'avoir clarifié cela. J'avais un problème similaire.

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

Alors maintenant, après les avoir commentés, je suis passé et a remplacé chaque occurrence avec, par exemple

self.firstAsset Il semble que je pourrais aussi utiliser firstAsset, mais je manque de voir le "" trop souvent.

0
Harry McGovern