web-dev-qa-db-fra.com

Pourquoi utiliseriez-vous un ivar?

Je vois habituellement cette question posée de l’autre côté, telle que Chaque ivar doit-il être une propriété? (et j’aime la réponse de bbum à ce Q).

J'utilise des propriétés presque exclusivement dans mon code. Cependant, de temps en temps, je travaille avec un sous-traitant qui développe sur iOS depuis longtemps et qui est un programmeur de jeu traditionnel. Il écrit un code qui ne déclare presque aucune propriété et s’appuie sur ivars. Je suppose qu'il le fait parce que 1.) il y est habitué, car les propriétés n'existaient pas avant Objective C 2.0 (Oct '07) et 2.) pour un gain de performances minimal lié au fait de ne pas passer par un getter/setter.

Alors qu'il écrit du code qui ne coule pas, je préférerais quand même qu'il utilise les propriétés plutôt que les ivars. Nous en avons parlé et il ne voit plus ou moins la raison d'utiliser les propriétés, car nous n'utilisions pas KVO et il avait l'habitude de s'occuper des problèmes de mémoire.

Ma question est plus ... Pourquoi voudriez-vous jamais utiliser une période d'ivar - expérimentée ou non. Y a-t-il vraiment une si grande différence de performances que l'utilisation d'un ivar serait justifiée?

Également à titre de clarification, je remplace les setters et les getters selon les besoins et utilise l'ivar qui est en corrélation avec cette propriété à l'intérieur du getter/setter. Cependant, en dehors d’un getter/setter ou d’un init, j’utilise toujours le self.myProperty syntaxe.


Modifier 1

J'apprécie toutes les bonnes réponses. L'une des solutions que je voudrais aborder et qui semble incorrecte est qu'avec un ivar, vous obtenez une encapsulation alors qu'avec une propriété, ce n'est pas le cas. Il suffit de définir la propriété dans une continuation de classe. Cela cachera la propriété des étrangers. Vous pouvez également déclarer la propriété en lecture seule dans l'interface et la redéfinir en lecture/écriture dans l'implémentation, comme suit:

// readonly for outsiders
@property (nonatomic, copy, readonly) NSString * name;

et avoir dans la suite de la classe:

// readwrite within this file
@property (nonatomic, copy) NSString * name;

Pour l'avoir complètement "privé", ne le déclarez que dans la suite de la classe.

149
Sam

Encapsulation

Si l'ivar est privé, les autres parties du programme ne peuvent pas l'obtenir aussi facilement. Avec une propriété déclarée, les personnes intelligentes peuvent accéder et muter assez facilement via les accesseurs.

Performance

Oui, cela peut faire la différence dans certains cas. Certains programmes ont des contraintes où ils ne peuvent utiliser aucune messagerie objc dans certaines parties du programme (pensez en temps réel). Dans d'autres cas, vous voudrez peut-être y accéder directement pour plus de rapidité. Dans d'autres cas, c'est parce que objc messaging agit comme un pare-feu d'optimisation. Enfin, cela peut réduire le nombre d'opérations de comptage de références et minimiser l'utilisation de mémoire maximale (si cela est effectué correctement).

Types non essentiels

Exemple: Si vous avez un type C++, l'accès direct est parfois la meilleure approche. Le type peut ne pas être copiable ou la copie peut ne pas être simple.

Multithreading

Beaucoup de vos ivars sont codépendants. Vous devez vous assurer de l'intégrité de vos données dans un contexte multithread. Ainsi, vous pouvez favoriser un accès direct à plusieurs membres dans les sections critiques. Si vous vous en tenez aux accesseurs pour les données dépendantes du code, vos verrous doivent généralement être réentrants et vous finirez souvent par faire beaucoup plus d'acquisitions (beaucoup plus parfois).

Exactitude du programme

Comme les sous-classes peuvent remplacer n'importe quelle méthode, vous constaterez peut-être qu'il existe une différence sémantique entre l'écriture sur l'interface et la gestion appropriée de votre état. L'accès direct pour l'exactitude du programme est particulièrement courant dans les états partiellement construits - dans vos initialiseurs et dans dealloc, il est préférable d'utiliser l'accès direct. Vous pouvez également trouver ce point commun dans les implémentations d'un accesseur, d'un constructeur pratique, de copy, mutableCopy et d'implémentations d'archivage/sérialisation.

C'est aussi plus fréquent quand on passe de tout a un état d'esprit d'accesseur public en lecture-écriture à un qui cache bien ses détails/données de mise en œuvre. Parfois, vous devez éviter correctement les effets secondaires qu'une substitution de sous-classe peut introduire afin de prendre les mesures qui s'imposent.

Taille binaire

Déclarer tout ce qui est readwrite par défaut entraîne généralement de nombreuses méthodes d'accès dont vous n'avez jamais besoin, lorsque vous considérez l'exécution de votre programme un instant. Cela ajoutera donc de la graisse à votre programme et vos temps de chargement également.

Minimise la complexité

Dans certains cas, il est tout simplement inutile d'ajouter + type + de maintenir tout cet échafaudage supplémentaire pour une variable simple telle qu'un bool privé qui est écrit dans une méthode et lu dans une autre.


Ce n'est pas du tout de dire que l'utilisation de propriétés ou d'accesseurs est une mauvaise chose: chacune présente des avantages et des restrictions importantes. Comme beaucoup de langues et d'approches de conception OO), vous devez également privilégier les accesseurs avec une visibilité appropriée dans ObjC. Il y aura des moments où vous devrez dévier. Pour cette raison, je pense qu'il est souvent préférable de restreindre l'accès direct. accède à l'implémentation qui déclare l'ivar (par exemple, le déclare @private).


re Edit 1:

La plupart d'entre nous ont mémorisé comment appeler dynamiquement un accesseur masqué (tant que nous connaissons le nom…). Pendant ce temps, la plupart d’entre nous n’avons pas mémorisé comment accéder correctement aux ivars qui ne sont pas visibles (au-delà de KVC). La suite de la classe aide , mais elle introduit des vulnérabilités.

Cette solution de contournement est évidente:

if ([obj respondsToSelector:(@selector(setName:)])
  [(id)obj setName:@"Al Paca"];

Maintenant, essayez avec un ivar seulement, et sans KVC.

98
justin

Pour moi, c'est généralement la performance. Accéder à un ivar d'un objet est aussi rapide que d'accéder à un membre de structure en C en utilisant un pointeur sur la mémoire contenant une telle structure. En fait, les objets Objective-C sont essentiellement des structures C situées dans une mémoire allouée dynamiquement. C’est généralement aussi rapide que votre code peut l’obtenir. Même un code d’assemblage optimisé à la main ne peut être plus rapide que cela.

Accéder à un ivar via un getter/paramètre implique un appel de méthode Objective-C, qui est beaucoup plus lent (au moins 3 à 4 fois) qu'un appel de fonction C "normal" et même un appel de fonction C normal serait déjà plusieurs fois plus lent que accéder à un membre struct. Selon les attributs de votre propriété, l’implémentation setter/getter générée par le compilateur peut impliquer un autre appel de la fonction C aux fonctions objc_getProperty/objc_setProperty, car ils devront retain/copy/autorelease les objets selon les besoins et effectuer le verrouillage des propriétés atomiques le cas échéant. Cela peut facilement devenir très coûteux et je ne parle pas d’être 50% plus lent.

Essayons ça:

CFAbsoluteTime cft;
unsigned const kRuns = 1000 * 1000 * 1000;

cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
    testIVar = i;
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"1: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);

cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
    [self setTestIVar:i];
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"2: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);

Sortie:

1: 23.0 picoseconds/run
2: 98.4 picoseconds/run

Ceci est 4,28 fois plus lent et il s’agit d’une primitive non atomique int, à peu près la meilleur des cas; la plupart des autres cas sont encore pires (essayez un système atomique NSString * propriété!). Donc, si vous pouvez vivre avec le fait que chaque accès à ivar est 4 à 5 fois plus lent que prévu, l'utilisation de propriétés convient (du moins en termes de performances), cependant, il existe de nombreuses situations dans lesquelles une telle baisse de performances est complètement inacceptable.

Mise à jour 2015-10-20

Certaines personnes prétendent que ce n'est pas un problème réel, le code ci-dessus est purement synthétique et vous ne le remarquerez jamais dans une application réelle. Bon alors, essayons un échantillon du monde réel.

Le code ci-dessous définit les objets Account. Un compte a des propriétés qui décrivent le nom (NSString *), sexe (enum) et âge (unsigned) de son propriétaire, ainsi qu’un solde (int64_t). Un objet de compte a une méthode init et un compare: méthode. Le compare: La méthode est définie comme suit: ordres des femmes avant les hommes, ordre des noms alphabétique, ordres des jeunes avant l’âge, ordres de solde faibles à élevés.

En réalité, il existe deux classes de compte, AccountA et AccountB. Si vous regardez leur implémentation, vous remarquerez qu’elles sont presque entièrement identiques, à une exception près: le compare: méthode. AccountA les objets accèdent à leurs propres propriétés par méthode (getter), tandis que AccountB objets accèdent leurs propres propriétés par ivar. C'est vraiment la seule différence! Ils accèdent tous les deux aux propriétés de l'autre objet avec lequel effectuer une comparaison (l'accès à ivar ne serait pas sûr! Et si l'autre objet est une sous-classe et qu'il a remplacé le getter?). Notez également que l'accès à vos propres propriétés en tant qu'ivars ne rompt pas l'encapsulation (les ivars ne sont toujours pas publics).

La configuration du test est très simple: créez 1 million de comptes aléatoires, ajoutez-les à un tableau et triez ce tableau. C'est ça. Bien sûr, il existe deux tableaux, un pour les objets AccountA et un pour les objets AccountB. Les deux tableaux sont remplis avec des comptes identiques (même source de données). Nous calculons combien de temps il faut pour trier les tableaux.

Voici le résultat de plusieurs essais que j'ai faits hier:

runTime 1: 4.827070, 5.002070, 5.014527, 5.019014, 5.123039
runTime 2: 3.835088, 3.804666, 3.792654, 3.796857, 3.871076

Comme vous pouvez le constater, trier le tableau de AccountB objets est toujours significatif plus rapidement que trier le tableau de AccountA objets.

Quiconque affirme que des différences d’exécution allant jusqu’à 1,32 seconde ne font aucune différence ne devrait jamais faire de programmation UI. Si je souhaite modifier l'ordre de tri d'une grande table, par exemple, les différences de temps comme celles-ci font une énorme différence pour l'utilisateur (différence entre une interface utilisateur acceptable et une interface utilisateur lente).

Dans ce cas également, l'exemple de code est le seul travail réel effectué ici, mais combien de fois votre code est-il juste un petit engrenage d'une horloge compliquée? Et si chaque vitesse ralentit le processus dans son ensemble, qu'est-ce que cela signifie pour la vitesse de tout le mouvement d'horlogerie à la fin? Surtout si une étape de travail dépend de la sortie d'une autre, ce qui signifie que toutes les inefficiences résumeront. La plupart des inefficacités ne sont pas un problème en elles-mêmes, c'est leur somme qui devient un problème pour tout le processus. Et un tel problème n’est pas évident pour un profileur, car il s’agit de trouver des points chauds critiques, mais aucune de ces inefficiences n’est en soi un point chaud. Le temps de calcul est juste moyennement réparti entre eux, et pourtant chacun d’eux n’en possède qu’une infime fraction, il semble donc une perte de temps totale de l’optimiser. Et il est vrai qu'optimiser un seul d'entre eux ne servirait à rien, les optimiser tous peut considérablement aider.

Et même si vous ne pensez pas en termes de temps CPU, car vous pensez que perdre du temps CPU est totalement acceptable, après tout, "c'est gratuit", alors qu'en est-il des coûts d'hébergement de serveur causés par la consommation d'énergie? Qu'en est-il de l'autonomie de la batterie des appareils mobiles? Si vous voulez écrire deux fois la même application mobile (par exemple, votre propre navigateur Web mobile), une fois une version où toutes les classes accèdent à leurs propres propriétés uniquement par des accesseurs, et une fois où toutes les classes n'y accèdent que par ivars, l'utilisation de la première continuera sans aucun doute à épuiser la batterie beaucoup plus rapidement que l’utilisation de la seconde, bien qu’elles soient équivalentes sur le plan fonctionnel et que, pour l’utilisateur, la seconde se sentirait probablement même un peu plus rapide.

Maintenant, voici le code pour votre main.m fichier (le code repose sur l’activation de ARC et veillez à utiliser l’optimisation lors de la compilation pour obtenir un effet complet):

#import <Foundation/Foundation.h>

typedef NS_ENUM(int, Gender) {
    GenderMale,
    GenderFemale
};


@interface AccountA : NSObject
    @property (nonatomic) unsigned age;
    @property (nonatomic) Gender gender;
    @property (nonatomic) int64_t balance;
    @property (nonatomic,nonnull,copy) NSString * name;

    - (NSComparisonResult)compare:(nonnull AccountA *const)account;

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance;
@end


@interface AccountB : NSObject
    @property (nonatomic) unsigned age;
    @property (nonatomic) Gender gender;
    @property (nonatomic) int64_t balance;
    @property (nonatomic,nonnull,copy) NSString * name;

    - (NSComparisonResult)compare:(nonnull AccountB *const)account;

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance;
@end


static
NSMutableArray * allAcocuntsA;

static
NSMutableArray * allAccountsB;

static
int64_t getRandom ( const uint64_t min, const uint64_t max ) {
    assert(min <= max);
    uint64_t rnd = arc4random(); // arc4random() returns a 32 bit value only
    rnd = (rnd << 32) | arc4random();
    rnd = rnd % ((max + 1) - min); // Trim it to range
    return (rnd + min); // Lift it up to min value
}

static
void createAccounts ( const NSUInteger ammount ) {
    NSArray *const maleNames = @[
        @"Noah", @"Liam", @"Mason", @"Jacob", @"William",
        @"Ethan", @"Michael", @"Alexander", @"James", @"Daniel"
    ];
    NSArray *const femaleNames = @[
        @"Emma", @"Olivia", @"Sophia", @"Isabella", @"Ava",
        @"Mia", @"Emily", @"Abigail", @"Madison", @"Charlotte"
    ];
    const NSUInteger nameCount = maleNames.count;
    assert(maleNames.count == femaleNames.count); // Better be safe than sorry

    allAcocuntsA = [NSMutableArray arrayWithCapacity:ammount];
    allAccountsB = [NSMutableArray arrayWithCapacity:ammount];

    for (uint64_t i = 0; i < ammount; i++) {
        const Gender g = (getRandom(0, 1) == 0 ? GenderMale : GenderFemale);
        const unsigned age = (unsigned)getRandom(18, 120);
        const int64_t balance = (int64_t)getRandom(0, 200000000) - 100000000;

        NSArray *const nameArray = (g == GenderMale ? maleNames : femaleNames);
        const NSUInteger nameIndex = (NSUInteger)getRandom(0, nameCount - 1);
        NSString *const name = nameArray[nameIndex];

        AccountA *const accountA = [[AccountA alloc]
            initWithName:name age:age gender:g balance:balance
        ];
        AccountB *const accountB = [[AccountB alloc]
            initWithName:name age:age gender:g balance:balance
        ];

        [allAcocuntsA addObject:accountA];
        [allAccountsB addObject:accountB];
    }
}


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        @autoreleasepool {
            NSUInteger ammount = 1000000; // 1 Million;
            if (argc > 1) {
                unsigned long long temp = 0;
                if (1 == sscanf(argv[1], "%llu", &temp)) {
                    // NSUIntegerMax may just be UINT32_MAX!
                    ammount = (NSUInteger)MIN(temp, NSUIntegerMax);
                }
            }
            createAccounts(ammount);
        }

        // Sort A and take time
        const CFAbsoluteTime startTime1 = CFAbsoluteTimeGetCurrent();
        @autoreleasepool {
            [allAcocuntsA sortedArrayUsingSelector:@selector(compare:)];
        }
        const CFAbsoluteTime runTime1 = CFAbsoluteTimeGetCurrent() - startTime1;

        // Sort B and take time
        const CFAbsoluteTime startTime2 = CFAbsoluteTimeGetCurrent();
        @autoreleasepool {
            [allAccountsB sortedArrayUsingSelector:@selector(compare:)];
        }
        const CFAbsoluteTime runTime2 = CFAbsoluteTimeGetCurrent() - startTime2;

        NSLog(@"runTime 1: %f", runTime1);
        NSLog(@"runTime 2: %f", runTime2);
    }
    return 0;
}



@implementation AccountA
    - (NSComparisonResult)compare:(nonnull AccountA *const)account {
        // Sort by gender first! Females prior to males.
        if (self.gender != account.gender) {
            if (self.gender == GenderFemale) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Otherwise sort by name
        if (![self.name isEqualToString:account.name]) {
            return [self.name compare:account.name];
        }

        // Otherwise sort by age, young to old
        if (self.age != account.age) {
            if (self.age < account.age) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Last ressort, sort by balance, low to high
        if (self.balance != account.balance) {
            if (self.balance < account.balance) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // If we get here, the are really equal!
        return NSOrderedSame;
    }

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance
    {
        self = [super init];
        assert(self); // We promissed to never return nil!

        _age = age;
        _gender = gender;
        _balance = balance;
        _name = [name copy];

        return self;
    }
@end


@implementation AccountB
    - (NSComparisonResult)compare:(nonnull AccountA *const)account {
        // Sort by gender first! Females prior to males.
        if (_gender != account.gender) {
            if (_gender == GenderFemale) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Otherwise sort by name
        if (![_name isEqualToString:account.name]) {
            return [_name compare:account.name];
        }

        // Otherwise sort by age, young to old
        if (_age != account.age) {
            if (_age < account.age) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Last ressort, sort by balance, low to high
        if (_balance != account.balance) {
            if (_balance < account.balance) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // If we get here, the are really equal!
        return NSOrderedSame;
    }

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance
    {
        self = [super init];
        assert(self); // We promissed to never return nil!

        _age = age;
        _gender = gender;
        _balance = balance;
        _name = [name copy];

        return self;
    }
@end
73
Mecki

Sémantique

  • Quoi @property peut exprimer que ivars ne peut pas: nonatomic et copy.
  • Quels ivars peuvent exprimer que @property ne peut pas:
    • @protected: public sur sous-classes, privé extérieur.
    • @package: public sur les frameworks sur 64 bits, privé à l'extérieur. Pareil que @public sur 32 bits. Reportez-vous à la section Contrôle de l'accès aux variables 64 bits de la classe et de l'instance d'Apple .
    • Qualifications. Par exemple, des tableaux de références d’objets forts: id __strong *_objs.

Performance

Petite histoire: les ivars sont plus rapides, mais peu importe la plupart des utilisations. Les propriétés nonatomic n'utilisent pas de verrous, mais ivar direct est plus rapide car il ignore l'appel des accesseurs. Pour plus de détails, lisez ce qui suit email à l'adresse lists.Apple.com.

Subject: Re: when do you use properties vs. ivars?
From: John McCall <email@hidden>
Date: Sun, 17 Mar 2013 15:10:46 -0700

Les propriétés affectent les performances de nombreuses manières:

  1. Comme indiqué précédemment, l'envoi d'un message pour effectuer un chargement/stockage est plus lent que le simple chargement/stockage en ligne .

  2. Envoyer un message pour charger/stocker est aussi un peu plus de code qui doit être conservé dans i-cache: même si le getter/setter ajouté zéro instruction supplémentaire au-delà du chargement/stockage, il y aurait une demi-douzaine d'instructions supplémentaires dans l'appelant pour configurer l'envoi du message et gérer le résultat.

  3. L'envoi d'un message force le maintien d'une entrée pour ce sélecteur dans le cache de la méthode , et cette mémoire reste généralement dans d-cache. Cela augmente le temps de lancement, augmente l'utilisation de mémoire statique de votre application et rend les changements de contexte plus difficiles. Comme le cache de méthode est spécifique à la classe dynamique pour un objet, ce problème augmente au fur et à mesure que vous utilisez KVO sur celui-ci.

  4. L'envoi d'un message force toutes les valeurs de la fonction à être déversées dans la pile (ou conservées dans des registres de sauvegarde des appels, ce qui signifie simplement qu'elles sont renversées à un autre moment ).

  5. L'envoi d'un message peut avoir des effets secondaires arbitraires et par conséquent

    • oblige le compilateur à réinitialiser toutes ses hypothèses sur la mémoire non locale
    • ne peut pas être hissé, coulé, réordonné, fusionné ou éliminé.

  6. Dans ARC, le résultat d'un message envoyé sera toujours conservé , que ce soit par l'appelé ou l'appelant, même pour les retours +0: même si la méthode ne conserve pas/autorelease son résultat, l'appelant ne le sait pas et doit essayer de prendre des mesures pour éviter que le résultat ne soit auto-publié. Cela ne peut jamais être éliminé car les envois de messages ne sont pas analysables statiquement.

  7. Dans ARC, puisqu’une méthode de définition prend généralement son argument à +0, il n’existe aucun moyen de "transférer" une retenue de cet objet (qui, comme discuté ci-dessus, a généralement) dans ivar, donc la valeur doit généralement être retenue/validée deux fois .

Bien sûr, rien de tout cela ne signifie qu'elles sont toujours mauvaises - il y a beaucoup de bonnes raisons d'utiliser des propriétés. N'oubliez pas que, comme beaucoup d'autres fonctionnalités linguistiques, elles ne sont pas gratuites.


John.

9
Jano

La raison la plus importante est le OOP concept de masquage d'informations: si vous exposez tout via des propriétés et permettez ainsi à des objets externes de jeter un œil sur les éléments internes d'un autre objet, vous ferez alors l’utilisation de ces fonctions internes et compliquent donc le changement de mise en œuvre.

Le gain "de performances minimales" peut rapidement résumer et devenir un problème. Je sais par expérience; Je travaille sur une application qui prend vraiment les iDevices à leur limite et nous devons donc éviter les appels de méthodes inutiles (bien sûr, uniquement dans la mesure du possible). Pour nous aider dans cet objectif, nous évitons également la syntaxe à points, car il est difficile de voir le nombre d'appels de méthode à première vue: par exemple, combien d'appels de méthode font l'expression self.image.size.width déclencheur? En revanche, vous pouvez immédiatement dire avec [[self image] size].width.

De plus, avec un nom d'ivar correct, KVO est possible sans propriétés (IIRC, je ne suis pas un expert du KVO).

9
DarkDust

Propriétés/variables d'instance est un compromis, à la fin, le choix revient à l'application.

Encapsulation/Masquage des informations C’est une bonne chose du point de vue de la conception, des interfaces étroites et une liaison minimale sont ce qui rend le logiciel maintenable et compréhensible. Il est assez difficile dans Obj-C de cacher quoi que ce soit, mais les variables d'instance déclarées dans l'implémentation sont aussi proches que possible.

Performance Alors que "l'optimisation prématurée" est une mauvaise chose, écrire du code peu performant simplement parce que vous le pouvez est au moins aussi mauvais. Il est difficile de discuter du fait qu'un appel de méthode est plus coûteux qu'un chargement ou un magasin, et en code de calcul intensif, le coût augmente rapidement.

Dans un langage statique avec des propriétés, telles que C #, les appels aux setters/getters peuvent souvent être optimisés par le compilateur. Cependant, Obj-C est dynamique et la suppression de tels appels est beaucoup plus difficile.

Abstraction Un argument contre les variables d'instance dans Obj-C était traditionnellement la gestion de la mémoire. Avec MRC, les variables d’instance requièrent que les appels conservés/validés/libérés automatiquement soient répartis dans le code; les propriétés (synthétisées ou non) gardent le code MRC au même endroit - principe de l’abstraction qui est une bonne chose (TM). Cependant, avec GC ou ARC, cet argument disparaît. L'abstraction pour la gestion de la mémoire n'est plus un argument contre variables d'instance.

6
CRD

Compatibilité ascendante était un facteur pour moi. Je ne pouvais utiliser aucune fonctionnalité d'Objective-C 2.0 car je développais des logiciels et des pilotes d'imprimante devant fonctionner sous Mac OS X 10.3 en tant que partie intégrante d'une exigence. Je sais que votre question semblait ciblée sur iOS, mais je pensais que je partagerais toujours mes raisons pour ne pas utiliser les propriétés.

5
dreamlax

Les propriétés exposent vos variables à d'autres classes. Si vous avez juste besoin d'une variable qui ne concerne que la classe que vous créez, utilisez une variable d'instance. Voici un petit exemple: les classes XML d’analyse de flux RSS, entre autres, parcourent de nombreuses méthodes de délégation, etc. Il est pratique de disposer d’une instance de NSMutableString pour stocker le résultat de chaque passage différent de l’analyse. Il n'y a aucune raison pour qu'une classe externe ait besoin d'accéder ou de manipuler cette chaîne. Donc, il suffit de le déclarer dans l'en-tête ou en privé et d'y accéder tout au long de la classe. Il peut être utile de définir une propriété afin d’éviter tout problème de mémoire, en utilisant self.mutableString pour appeler le getter/setters.

5
Justin