方法/IBAction 陷入无限循环。仍然没有成功

发布于 2024-08-09 08:24:52 字数 6524 浏览 7 评论 0原文

现在,这可能听起来像我之前的问题/问题,但我已经改变并尝试了其他问题中回答的一些事情,试图使其发挥作用,但我仍然遇到同样的问题。

我正在观察 NSManagedObject 子类中的核心数据属性,以及当属性更改时调用另一个方法的方法,但在该方法中它添加了核心数据对象,该对象触发 KVO 方法,该方法再次触发该方法等等。或者看起来是这样,我对此不太确定,因为似乎发生了不同的事情,这是一系列事件...

  1. 我单击与 iCal 同步的按钮(这在 IBAction 中,其代码与方法syncKVO 中的代码完全相同) 。这个同步效果很好。
  2. 我将一个对象添加到我的大纲视图中。一切都很好。
  3. 我更改了它的名称,这会触发与 iCal 同步的 KVO 声明(因为我更改了“名称”属性)。工作正常。
  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"];
}

KVO 声明(在应用程序委托内)...

- (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];

但这样它第一次工作,但第二次停止该方法,说它无法删除观察者,因为它没有观察,这是没有意义的,因为我第一次再次添加了观察者。这就是为什么我将此代码保留在实际方法之外,否则它将在第二次同步时停止。

我不确定这是怎么回事,我想我已经尝试了一切。出什么问题了?

任何帮助将不胜感激。

Now this may sound like my earlier problem/question but I've changed and tried a few things that were answered in my other questions to try to make it work, but I've still got the same problem.

I am observing a core data property from within a NSManagedObject sub-class and the method that gets called when the property changes calls another method but in this method it adds Core Data objects which triggers the KVO method which triggers the method again and so forth. Or so it seems, I'm not too sure about that because something different seems to happen, here is the series of events …

  1. I click a button syncing with iCal (this in an IBAction with the exact same code thats in the method syncKVO). This sync works fine.
  2. I add an object to my outline view. All is well.
  3. I change its name which triggers the KVO Declaration (because I changed the 'name' property) which syncs with iCal. Works fine.
  4. I delete the object I just added and somehow it triggers the KVO declaration (thus triggering the method) and puts me into an infinite loop.

Now for some code.

Code inside the NSManagedObject Subclass (called 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"];
}

The KVO Declaration (inside the App Delegate) …

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

The Method (also inside the App Delegate)…

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

What I've tried …

Filtering the Keypaths that call the KVO Declaration with

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

Detaching and reattaching observers with

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

but with that it works the first time but stops the method the second time saying that it cannot remove the observer because it is not observing, which doesn't make sense because I added the observer again the first time. That is why I left this code out of the actual method else it would stop on the second sync.

I'm not sure whats going on with this, I think I've tried everything. Whats gone wrong?

Any help would be greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不爱素颜 2024-08-16 08:24:52

问题可能出在这里:

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

}

每当“name”发生某些事情时,无论“name”实际发生了什么,您都会调用syncKVO:。我建议您开始使用对象、更改和上下文参数来确定刚刚发生了什么以及应该采取什么操作(如果有)。

顺便说一句,向应用程序委托添加大量内容并不被认为是好的做法。您可能希望将所有这些同步内容放入适当的控制器类中,并在需要时调用 [NSApp delegate]。

The problem might be here:

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

}

You call syncKVO: everytime something happens to 'name' regardless of what it is that has actually happened to 'name'. I suggest you start using the object, change and context parameters to determine what has just happened and what action, if any, should be undertaken.

BTW, it's not considered good practice to add a lot of stuff to the app delegate. You might want to put all this syncing stuff into a proper controller class and call [NSApp delegate] when you need it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文