web-dev-qa-db-fra.com

Méthodes de définition personnalisées dans Core-Data

J'ai besoin d'écrire une méthode de définition personnalisée pour un champ (nous l'appellerons foo) dans ma sous-classe de NSManagedObject. foo est défini dans le modèle de données et Xcode a généré automatiquement @property et @dynamic champs dans les fichiers .h et .m respectivement.

Si j'écris mon setter comme ceci:

- (void)setFoo: (NSObject *)inFoo {
    [super setFoo: inFoo];
    [self updateStuff];
}

puis je reçois un avertissement du compilateur lors de l'appel à super.

Sinon, si je fais cela:

- (void)setFoo: (NSObject *)inFoo {
    [super setValue: inFoo forKey: inFoo];
    [self updateStuff];
}

puis je me retrouve dans une boucle infinie.

Quelle est donc la bonne approche pour écrire un setter personnalisé pour une sous-classe de NSManagedObject?

69
Andrew Ebling

Voici la méthode Apple pour remplacer les propriétés NSManagedObject (sans casser KVO), dans votre .m:

@interface Transaction (DynamicAccessors)
- (void)managedObjectOriginal_setDate:(NSDate *)date;
@end

@implementation Transaction
@dynamic date;

- (void)setDate:(NSDate *)date
{
    // invoke the dynamic implementation of setDate (calls the willChange/didChange for you)
    [self managedObjectOriginal_setDate:(NSString *)date;

    // your custom code
}

managedObjectOriginal_propertyName est une méthode magique intégrée pour laquelle il suffit d'ajouter la définition. Comme vu au bas de cette page Quoi de neuf dans Core Data dans macOS 10.12, iOS 10.0, tvOS 10.0 et watchOS 3.

7
malhal

Selon la documentation , ce serait:

- (void) setFoo:(NSObject *)inFoo {
  [self willChangeValueForKey:@"foo"];
  [self setPrimitiveValue:inFoo forKey:@"foo"];
  [self didChangeValueForKey:@"foo"];
}

Ceci, bien sûr, ignore le fait que NSManagedObjects ne veut que NSNumbers, NSDates, NSDatas et NSStrings comme attributs.

Cependant, ce n'est peut-être pas la meilleure approche. Puisque vous voulez que quelque chose se produise lorsque la valeur de votre propriété foo change, pourquoi ne pas simplement l'observer avec Key Value Observing ? Dans ce cas, cela ressemble à "la voie à suivre pour KVO".

102
Dave DeLong

Voici comment je fais KVO sur l'attribut id d'un Photo : NSManagedObject. Si l'ID de la photo change, téléchargez la nouvelle photo.

#pragma mark NSManagedObject

- (void)awakeFromInsert {
    [self observePhotoId];
}

- (void)awakeFromFetch {
    [self observePhotoId];
}

- (void)observePhotoId {
    [self addObserver:self forKeyPath:@"id"
              options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:NULL];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"id"]) {
        NSString *oldValue = [change objectForKey:NSKeyValueChangeOldKey];
        NSString *newValue = [change objectForKey:NSKeyValueChangeNewKey];        
        if (![newValue isEqualToString:oldValue]) {
            [self handleIdChange];
        }
    }
}

- (void)willTurnIntoFault {
    [self removeObserver:self forKeyPath:@"id"];
}

#pragma mark Photo

- (void)handleIdChange {
    // Implemented by subclasses, but defined here to hide warnings.
    // [self download]; // example implementation
}
19
ma11hew28

Je pense qu'il y a une petite erreur: utiliser

 [self setPrimitiveValue:inFoo forKey:@"foo"];

au lieu de

 [self setPrimitiveFoo:inFoo];

cela fonctionne pour moi.

17
Martin Brugger

Voici comment vous faites les relations 1-n (et je suppose n-m):

Supposons que le nom de la relation est appelé "étudiants" dans un objet appelé "Ecole".

Vous devez d'abord définir les méthodes d'accesseur primitives pour NSMutableSet. Xcode ne les générera pas automatiquement pour vous.

@interface School(PrimitiveAccessors)
- (NSMutableSet *)primitiveStudents;
@end

Vous pouvez ensuite définir votre méthode d'accesseur. Ici, je vais remplacer le setter.

- (void)addStudentsObject:(Student *)student
{
  NSSet *changedObjects = [[NSSet alloc] initWithObjects:&student count:1];

  [self willChangeValueForKey:@"students"
              withSetMutation:NSKeyValueUnionSetMutation
                 usingObjects:changedObjects];

  [[self primitiveStudents] addObject:value];

  [self didChangeValueForKey:@"students"
             withSetMutation:NSKeyValueUnionSetMutation
                usingObjects:changedObjects];
}
0
David