2009-10-10 5 views
1

Ok, j'ai une IBAction qui se synchronise avec iCal et qui est également déclenchée par KVO de la propriété 'name' dans mon modèle de CD, de sorte que lorsque la propriété change, l'action est déclenchée. Ce qui se passe est qu'une fois que l'IBAction atteint la fin, il passe à la déclaration KVO qui déclenche à nouveau l'action et encore et encore, c'est là que la boucle se produit.IBAction est bloqué dans la boucle

Voici du code. Le IBAction ...

- (IBAction)sync:(id)sender { 
    [syncButton setTitle:@"Syncing..."]; 
    NSString *dateText = (@"Last Sync : %d", [NSDate date]); 
    [syncDate setStringValue:dateText]; 
    NSManagedObjectContext *moc = [self managedObjectContext]; 
    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"); 
} 

qui, lorsqu'il atteint la fin (en quelque sorte) déclenche la Déclaration KVO ...

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

Ce qui déclenche alors à nouveau la IBAction. C'est là que la boucle se produit lorsqu'elle est bloquée ici et qu'elle déclenche constamment l'Action.

Quelqu'un peut-il comprendre ce qui se passe sur terre ???

Répondre

3

Vous déclenchez le sélecteur sync: pour toute propriété conforme à la norme KVO.
Vous devez filtrer tout mot de passe qui est modifié avec votre méthode de synchronisation pour éviter la récursivité.

je ferais ce qui suit:

  • Définir un point d'arrêt et vérifiez la valeur de KeyPath pour savoir qui changent est de commencer la récursion.
  • Filtrer les changements pour keypaths qui déclenchent une récursion

ou

  • Détacher tous les observateurs lorsque la synchronisation: démarre, et les remettre en place lorsque vous avez terminé
+0

+1 Pas une mauvaise idée de détacher tous les observateurs au sommet de la synchronisation et les rattacher au fond. De cette façon, les modifications apportées pendant la synchronisation ne sont pas respectées, mais les modifications apportées au modèle ou au magasin de calendrier déclenchent toujours une synchronisation. – Abizern

+0

Donc, vous suggérez qu'au début de l'action j'ajoute '[JGManagedObject removeObserver: self forKeyPath: @" name "];' et à la fin ajoute '[JGManagedObject addObserver: self forKeyPath: @" name "options: NSKeyValueObservingOptionNew contexte : nul]; '? – Joshua

+0

Joshua: Ce ne sont pas des méthodes de classe, donc l'envoi de ces messages à la classe ne fonctionnera pas. –

Questions connexes