【问题标题】:Stopping a loop停止循环
【发布时间】:2009-10-26 17:37:55
【问题描述】:

正如我之前的问题中所解释的......

这段代码……

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

触发此代码……

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

因为我正在添加对象,并且在更改 Core Data 'name' 属性时会触发 KVO 方法。

如果syncKVO 方法触发了observeValueForKeyPath:ofObject:change:context: 方法,我需要停止它。我该怎么做?

【问题讨论】:

    标签: objective-c cocoa core-data infinite-loop key-value-observing


    【解决方案1】:

    您可以做的最简单的事情是使用一个实例变量来跟踪您是否正在同步并在设置时忽略观察者的更改。在syncKVO: 的开头和结尾停止并开始观察可能会更好,但这取决于您实际观察的内容:如果您正在观看大量收藏,您不想大规模退订和重新订阅。

    【讨论】:

    • 我试过这样做,但在调试器中出现错误。我将此代码添加到我的托管对象子类中,这是我观察核心数据名称属性的地方。 cld.ly/616yf 但我遇到了问题 cld.ly/1c6ye
    • 在类方法中 self 指的是 Class 对象(JGManagedObject 元类的实例),而不是单个实例,因此您的观察者管理调用正在监视类实例上的键,而不是在个别情况下。您应该将它们移动到实例方法中(最好在包含-syncKVO: 的控制器上,因为您的托管对象上已经提供了添加/删除观察者接口)。
    • 不,我没有;该代码仍然订阅来自类而不是实例的通知。您需要遍历您监视的实体并删除观察者,完成您的工作,然后继续添加它们。最好先看看你在哪里设置 K-V 观测;我没有看到你改变这里已经看过的任何东西的 name 属性,所以你必须在插入上下文或类似的东西时这样做。摘要:请显示更多代码。
    • 好的。我懂了。首先,我在我的 NSManagedObject 子类的 awakeFromFetch: 和 awakeFromInsert: 方法中添加了观察者。代码:snapplr.com/v2v6。我将如何遍历实体?或者我是否需要从大纲视图中遍历所有 NSObject 并删除每个对象的观察者?
    • 我想我想出了一个更好的解决方案,我将其作为单独的答案发布。如果你要继续沿着这条路走下去,我会说你需要跟踪你正在观察的实体,或者只是使用BOOL 标志技巧来暂时取消该观察者方法。我确实认为我的其他解决方案更简洁,并且可以让您完全避免使用 KVO(我认为这不是您要寻找的机器人)。
    【解决方案2】:

    查看您的代码,我想知道您是否真的想在保存实体时进行同步,而不是在对象键更改后立即进行。我认为您最好完全放弃观察并关注NSManagedObjectContextObjectsDidChangeNotification,使用CoreData documentation 中指定的userInfo 键的值来确定需要更新哪些实体。

    【讨论】:

    • 嗯。我将如何使用 NSManagedObjectContextObjectsDidChangeNotification,它会在发生变化时触发方法吗?另外,我正在观察一个核心数据属性,我可以对 NSManagedObjectContextObjectsDidChangeNotification 做同样的事情吗?
    • 另外,这样做会停止我的无限循环吗?
    • 有一个名为 NSManagedObjectContextObjectsDidChangeNotification 的 NSString 常量;您应该使用它而不是您在示例中显示的硬编码字符串。单独这样做不会停止你的无限循环——如果你正在修改实体,你仍然想取消订阅和重新订阅 syncKVO 中的通知(在保存更改之后)。顺便说一句,如果您将代码 sn-ps 发布为文本而不是图像,那就太好了(尝试gist.github.com)。
    • 好的,我认为这是我需要在我的应用程序委托中使用gist.github.com/221214 的代码,还是其中一些需要进入我的 NSManagedObject 子类?
    • 看起来不错。您可能希望在 syncKVO: 方法中检查通知的 userInfo 字典,以确保您确实必须同步。字典会告诉您添加、删除和修改了哪些对象,您可以将日历更新限制为仅影响相关对象的更新。
    猜你喜欢
    • 2017-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 2019-07-29
    相关资源
    最近更新 更多