Метод / IBAction застрял в бесконечном цикле. Все еще безуспешно - PullRequest
0 голосов
/ 24 октября 2009

Теперь это может звучать как моя предыдущая проблема / вопрос, но я изменил и попробовал несколько вещей, на которые были даны ответы в других моих вопросах, чтобы попытаться заставить это работать, но у меня все еще есть та же проблема.

Я наблюдаю свойство основных данных из подкласса NSManagedObject, и метод, который вызывается при изменении свойства, вызывает другой метод, но в этом методе он добавляет объекты Core Data, которые запускают метод KVO, который снова вызывает метод, и так далее. Или, кажется, я не слишком уверен в этом, потому что, кажется, происходит нечто иное, вот серия событий ...

  1. Я нажимаю кнопку синхронизации с iCal (это в IBAction с точно таким же кодом, который есть в методе syncKVO). Эта синхронизация работает нормально.
  2. Я добавляю объект в свой контурный вид. Все хорошо.
  3. Я изменяю его имя, которое вызывает объявление KVO (потому что я изменил свойство 'name'), которое синхронизируется с iCal. Работает отлично.
  4. Я удаляю только что добавленный объект, и каким-то образом он запускает объявление KVO (таким образом вызывая метод) и помещает меня в бесконечный цикл .

Теперь немного кода.

Код внутри подкласса NSManagedObject (называемый 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"];
}

Декларация КВО (внутри Делегата приложения)…

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

Метод (также внутри делегата приложения)…

- (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");
}

Что я пробовал ...

Фильтрация путей, вызывающих декларацию KVO, с помощью

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

Отсоединение и повторное подключение наблюдателей с помощью

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

но при этом он работает в первый раз, но останавливает метод во второй раз, говоря, что он не может удалить наблюдателя, потому что он не наблюдает, что не имеет смысла, потому что я снова добавил наблюдателя в первый раз. Вот почему я оставил этот код вне реального метода, иначе он остановился бы во второй синхронизации.

Я не уверен, что с этим происходит, я думаю, что все перепробовал. Что пошло не так?

Любая помощь будет принята с благодарностью.

1 Ответ

3 голосов
/ 24 октября 2009

Проблема может быть здесь:

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

}

Вы вызываете syncKVO: каждый раз, когда что-то случается с «именем», независимо от того, что на самом деле произошло с «именем». Я предлагаю вам начать использовать параметры объекта, изменения и контекста, чтобы определить, что только что произошло, и какие действия, если таковые имеются, следует предпринять.

Кстати, добавление большого количества материала в делегат приложения не считается хорошей практикой. Возможно, вы захотите поместить все эти вещи для синхронизации в соответствующий класс контроллера и вызывать [NSApp делегат], когда вам это нужно.

...