Question

Maintenant, cela peut ressembler à mon problème / question précédent, mais j’ai modifié et essayé quelques solutions auxquelles on a répondu dans mes autres questions pour essayer de le faire fonctionner, mais j’ai toujours le même problème.

J'observe une propriété de données principale dans une sous-classe NSManagedObject. La méthode appelée lorsque la propriété est modifiée appelle une autre méthode. Cette méthode ajoute cependant des objets Core Data qui déclenchent la méthode KVO qui déclenche à nouveau la méthode. etc. Ou alors, semble-t-il, je ne suis pas trop sûr à ce sujet car quelque chose de différent semble se produire, voici la série d’événements…

  1. Je clique sur un bouton synchronisé avec iCal (ceci dans une action IBAction avec exactement le même code que dans la méthode syncKVO). Cette synchronisation fonctionne bien.
  2. J'ajoute un objet à ma vue d'ensemble. Tout va bien.
  3. Je modifie son nom, ce qui déclenche la déclaration KVO (car j'ai modifié la propriété "name"), qui se synchronise avec iCal. Fonctionne bien.
  4. Je supprime l'objet que je viens d'ajouter et, d'une manière ou d'une autre, il déclenche la déclaration KVO (déclenchant ainsi la méthode) et me met dans une boucle infinie .

Maintenant pour du code.

Code dans la sous-classe NSManagedObject (appelé JGManagedObject)…

- (void) awakeFromFetch {
    [self addObserver:[NSApp delegate] forKeyPath:@"name" options:0 context:nil];
}

- (void) awakeFromInsert {
    [self addObserver:[NSApp delegate] forKeyPath:@"name" options:0 context:nil];
}

+ (void) addObserver{
    [self addObserver:[NSApp delegate] forKeyPath:@"name" options:0 context:nil];
}

+ (void) removeObserver{
    [self removeObserver:[NSApp delegate] forKeyPath:@"name"];
}

La déclaration KVO (à l'intérieur du délégué de l'application)…

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"name"]) {
        [self performSelector:@selector(syncKVO:)];
    }
}

La méthode (également à l'intérieur du délégué d'application)…

- (void)syncKVO:(id)sender {
    NSManagedObjectContext *moc = [self managedObjectContext];
    [syncButton setTitle:@"Syncing..."];
    NSString *dateText = (@"Last Sync : %d", [NSDate date]);
    [syncDate setStringValue:dateText];
    NSEntityDescription *entityDescription = [NSEntityDescription
                                              entityForName:@"projects" inManagedObjectContext:moc];
    NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
    [request setEntity:entityDescription];

    NSError *error = nil;
    NSArray *array = [moc executeFetchRequest:request error:&error];
    if (array == nil)
    {
        NSAlert *anAlert = [NSAlert alertWithError:error];
        [anAlert runModal];
    }
    NSArray *namesArray = [array valueForKey:@"name"];
    NSPredicate *predicate = [CalCalendarStore taskPredicateWithCalendars:[[CalCalendarStore defaultCalendarStore] calendars]];
    NSArray *tasksNo = [[CalCalendarStore defaultCalendarStore] tasksWithPredicate:predicate];
    NSArray *tasks = [tasksNo valueForKey:@"title"];
    NSMutableArray *namesNewArray = [NSMutableArray arrayWithArray:namesArray];
    [namesNewArray removeObjectsInArray:tasks];
    NSLog(@"%d", [namesNewArray count]);    
    NSInteger *popIndex = [calenderPopup indexOfSelectedItem];

    //Load the array
    CalCalendarStore *store = [CalCalendarStore defaultCalendarStore];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
    NSString *supportDirectory = [paths objectAtIndex:0];
    NSString *fileName = [supportDirectory stringByAppendingPathComponent:@"oldtasks.plist"];

    NSMutableArray *oldTasks = [[NSMutableArray alloc] initWithContentsOfFile:fileName];
    [oldTasks removeObjectsInArray:namesArray];
    NSLog(@"%d",[oldTasks count]);
    //Use the content
    NSPredicate* taskPredicate = [CalCalendarStore taskPredicateWithCalendars:[[CalCalendarStore defaultCalendarStore] calendars]];
    NSArray* allTasks = [[CalCalendarStore defaultCalendarStore] tasksWithPredicate:taskPredicate];

    // Get the calendar
    CalCalendar *calendar = [[store calendars] objectAtIndex:popIndex];
    // Note: you can change which calendar you're adding to by changing the index or by
    // using CalCalendarStore's -calendarWithUID: method    

    // Loop, adding tasks
    for(NSString *title in namesNewArray) {
        // Create task
        CalTask *task = [CalTask task];
        task.title = title;
        task.calendar = calendar;

        // Save task
        if(![[CalCalendarStore defaultCalendarStore] saveTask:task error:&error]) {
            NSLog(@"Error");
            // Diagnostic error handling
            NSAlert *anAlert = [NSAlert alertWithError:error];
            [anAlert runModal];
        }
    } 

    NSMutableArray *tasksNewArray = [NSMutableArray arrayWithArray:tasks];
    [tasksNewArray removeObjectsInArray:namesArray];
    NSLog(@"%d", [tasksNewArray count]);    
    for(NSString *title in tasksNewArray) {
        NSManagedObjectContext *moc = [self managedObjectContext];
        JGManagedObject *theParent = 
        [NSEntityDescription insertNewObjectForEntityForName:@"projects"
                                      inManagedObjectContext:moc];
        [theParent setValue:nil forKey:@"parent"];
        // This is where you add the title from the string array
        [theParent setValue:title forKey:@"name"]; 
        [theParent setValue:[NSNumber numberWithInt:0] forKey:@"position"];

    }

    for(CalTask* task in allTasks)
        if([oldTasks containsObject:task.title]) {
            [store removeTask:task error:nil];
        }

    // Create a predicate for an array of names.
    NSPredicate *mocPredicate = [NSPredicate predicateWithFormat:@"name IN %@", oldTasks];
    [request setPredicate:mocPredicate];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];

    // Execute the fetch request put the results into array
    NSArray *resultArray = [moc executeFetchRequest:request error:&error];
    if (resultArray == nil)
    {
        // Diagnostic error handling
        NSAlert *anAlert = [NSAlert alertWithError:error];
        [anAlert runModal];
    }

    // Enumerate through the array deleting each object.
    // WARNING, this will delete everything in the array, so you may want to put more checks in before doing this.
    for (JGManagedObject *objectToDelete in resultArray ) {
        // Delete the object.
        [moc deleteObject:objectToDelete];
    }
    //Save the array
    [namesArray writeToFile:fileName atomically:YES];
    [syncButton setTitle:@"Sync Now"];
    NSLog(@"Sync Completed");
}

Ce que j'ai essayé…

Filtrage des chemins de clé qui appellent la déclaration KVO avec

if ([keyPath isEqualToString:@"name"]) {
…
} 

Détacher et rattacher les observateurs avec

[JGManagedObject removeObserver];
//and
[JGManagedObject addObserver];

mais avec cela, cela fonctionne la première fois mais la méthode est arrêtée une deuxième fois en disant qu'elle ne peut pas supprimer l'observateur car elle n'observe pas, ce qui n'a pas de sens car j'ai ajouté l'observateur à nouveau la première fois. C’est pourquoi j’ai laissé ce code en dehors de la méthode actuelle, sinon il s’arrêterait à la deuxième synchronisation.

Je ne sais pas trop ce qui se passe, je pense avoir tout essayé. Qu'est-ce qui ne va pas?

Toute aide serait grandement appréciée.

Était-ce utile?

La solution

Le problème pourrait être ici:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"name"]) {
    [self performSelector:@selector(syncKVO:)];
}

}

Vous appelez syncKVO: chaque fois que quelque chose arrive à "nommer", peu importe ce qui est réellement arrivé à "nommer". Je vous suggère de commencer à utiliser les paramètres d'objet, de changement et de contexte pour déterminer ce qui vient de se passer et quelle action, le cas échéant, doit être entreprise.

Au fait, ce n’est pas une bonne pratique d’ajouter beaucoup de choses au délégué de l’application. Vous voudrez peut-être placer toutes ces tâches de synchronisation dans une classe de contrôleur appropriée et appeler [délégué NSApp] lorsque vous en aurez besoin.

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