Question

Ce petit détail, mais à chaque fois quelque chose que je charge paresseux que je suis pris sur le sujet. Est-ce que ces deux méthodes acceptables? Est-ce soit mieux? Supposons que la variable a néanmoins retenir.

Méthode n ° 1

(AnObject *)theObject{
    if (theObject == nil){
        theObject = [[AnObject createAnAutoreleasedObject] retain];
    }
    return theObject;
}

Méthode 2

(AnObject *)theObject{
    if (theObject == nil){
        self.theObject = [AnObject createAnAutoreleasedObject];
    }
    return theObject;
}

D'abord, je ne suis pas sûr si elle est OK pour accéder à une autre fonction accesseur dans un accesseur (ne vois pas pourquoi pas, cependant). Mais il semble que le réglage de la variable de classe sans passer par le poseur pourrait être tout aussi mauvais si le poseur fait (ou si la propriété est changé quelque chose en plus retenir et le getter est pas cochée) quelque chose de spécial.

Était-ce utile?

La solution

Les deux sont en fait assez fragiles et pas du tout identiques, en fonction de ce que les clients de la classe font. Ce qui les rend identiques est assez facile - voir ci-dessous - mais en le rendant moins fragile est plus difficile. Tel est le prix d'initialisation paresseux (et pourquoi je tente généralement d'éviter l'initialisation paresseuse de cette façon, préférant traiter l'initialisation des sous-systèmes en tant que partie d'application globale de gestion de l'État).

# 1, vous évitez le poseur et, ainsi, tout en observant le changement ne sera pas voir le changement. Par « observant », je fais référence à l'observation de la valeur de clé (y compris le cacao Manchettes, qui utilise KVO pour mettre à jour automatiquement l'interface utilisateur).

# 2, vous déclencherez la notification de changement, la mise à jour de l'interface utilisateur et par ailleurs exactement comme si le compositeur a été appelé.

Dans les deux cas, vous avez un potentiel pour une récursion infinie si l'initialisation de l'objet appelle le getter. Cela inclut, si tout observateur demande l'ancienne valeur en tant que partie de la notification de changement. Ne pas le faire.

Si vous allez utiliser soit la méthode, examiner soigneusement les conséquences. On a la possibilité de quitter l'application dans un état incohérent, car un changement d'état d'une propriété n'a pas notifié et l'autre a le potentiel de blocage.

Il vaut mieux éviter la question tout à fait. Voir ci-dessous.


Considérons (la collecte des déchets sur, outil standard de ligne de commande de cacao:

#import <Foundation/Foundation.h>

@interface Foo : NSObject
{
    NSString *bar;
}
@property(nonatomic, retain) NSString *bar;
@end
@implementation Foo
- (NSString *) bar
{
    if (!bar) {
        NSLog(@"[%@ %@] lazy setting", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
        [self willChangeValueForKey: @"bar"];
        bar = @"lazy value";
        [self didChangeValueForKey: @"bar"];
    }
    return bar;
}

- (void) setBar: (NSString *) aString
{
    NSLog(@"[%@ %@] setting value %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aString);
    bar = aString;
}
@end

@interface Bar:NSObject
@end
@implementation Bar
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
{
    NSLog(@"[%@ %@] %@ changed\n\tchange:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), keyPath, change);
}
@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Foo *foo = [Foo new];
    Bar *observer = [Bar new];
    CFRetain(observer);
    [foo addObserver:observer forKeyPath:@"bar"
             options: NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionNew
             context:NULL];
    foo.bar;
    foo.bar = @"baz";
    CFRelease(observer);

    [pool drain];
    return 0;
}

Cela ne se bloque pas. Il vomit:

2010-09-15 12:29:18.377 foobar[27795:903] [Foo bar] lazy setting
2010-09-15 12:29:18.396 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    notificationIsPrior = 1;
}
2010-09-15 12:29:18.397 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    new = "lazy value";
}
2010-09-15 12:29:18.400 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    notificationIsPrior = 1;
}
2010-09-15 12:29:18.400 foobar[27795:903] [Foo setBar:] setting value baz
2010-09-15 12:29:18.401 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    new = baz;
}

Si vous deviez ajouter NSKeyValueObservingOptionOld à la liste d'options pour l'observation, il fait très bien hang.

Pour en revenir à un commentaire que je disais plus tôt; la meilleure solution est de pas faire l'initialisation paresseuse comme une partie de votre getter / setter . Il est trop à grains fins. Vous êtes bien mieux gérer votre état graphique d'objet à un niveau supérieur et, comme une partie de cela, une transition d'état qui est essentiellement de la « Yo, je vais utiliser ce sous-système maintenant chaud ce mauvais garçon up! " cela fait l'initialisation paresseuse.

Autres conseils

Ces méthodes ne sont jamais identiques. Le premier est à droite, tandis que le second est mal ! Un getter ne peut jamais appeler will/didChangeValueForKey: et donc pas non plus le compositeur. Cela conduira à l'infini récursion si cette propriété est observée.

Et d'ailleurs, il n'y a pas de changement d'état d'observer quand le membre est initialisé. Vous demandez à votre objet pour le theObject et vous l'obtenez. Lorsque cela est créé est un détail de mise en œuvre et sans intérêt pour le monde extérieur.

Si vous connaissez la méthode setter de propriété est un normalisateur de retenue, ils sont identiques. Sinon, vous devez décider si doit être invoqué d'autres comportements du compositeur au cours de cette opération. Si vous ne connaissez pas, il est plus sûr d'utiliser le setter, car son comportement peut être important. Ne pas transpirer.

ce sont essentiellement deux identiques, il est vraiment juste à vous de choisir celui qui est le mieux pour votre cas. Vous avez déjà vraiment décrit les avantages / inconvénients sur l'utilisation de la syntaxe de la propriété.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top