【问题标题】:SEGV_ACCERR and crash app sometime on NSOperation when do [self.managedObjectContext save:&error]SEGV_ACCERR 和 NSOperation 上的某个应用程序崩溃 [self.managedObjectContext save:&error]
【发布时间】:2013-01-13 01:14:55
【问题描述】:

我有一个问题我无法理解如何处理,因为它不符合逻辑。

我有一些NSOperations 同时运行。例如,

- (void)main
{
    @autoreleasepool
    {        
        AppDelegate *appController = (AppDelegate *)[[UIApplication sharedApplication] delegate];

        self.managedObjectContext = [[NSManagedObjectContext alloc] init];
        [self.managedObjectContext setUndoManager:nil];
        [self.managedObjectContext setPersistentStoreCoordinator: [appController persistentStoreCoordinator]];

        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 
        [nc addObserver:self
               selector:@selector(mergeChanges:) 
                   name:NSManagedObjectContextDidSaveNotification
                 object:self.managedObjectContext];
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription 
                                       entityForName:@"Entity" inManagedObjectContext:self.managedObjectContext];
        [fetchRequest setEntity:entity];

        [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K != %@",@"number1",[NSNumber numberWithInt:2]]];

        NSError *error;
        NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

        for (NSManagedObject *obj in fetchedObjects) {

            //Do Something with managed object then save        
            NSError *error = nil;
            //[episode release];
            if (![self.managedObjectContext save:&error]) {
                // Replace this implementation with code to handle the error appropriately.
                // abort() causes the application to generate a crash log and terminate.
                // You should not use this function in a shipping application, although it may be useful
                // during development. If it is not possible to recover from the error, display an alert
                // panel that instructs the user to quit the application by pressing the Home button.
                //
                NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            }
        }
    }
}

- (void)mergeChanges:(NSNotification *)notification
{
    AppDelegate *appController = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *mainContext = [appController managedObjectContext];

    // Merge changes into the main context on the main thread
    [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
                              withObject:notification
                           waitUntilDone:YES];  
} 

这是我的典型NSOperation,它同时工作并更新核心数据中的对象,有时没有解释应用程序崩溃,我在这一行收到该错误:

if (![self.managedObjectContext save:&error])

在我的崩溃报告中,我的问题是,有没有办法防止应用崩溃并修复错误?我可以在执行save 时使用@syncronized 吗?这是由于不同的线程和不同的对象造成的吗?我该如何解决这个问题?

【问题讨论】:

    标签: iphone core-data nsmanagedobjectcontext nsoperation


    【解决方案1】:

    移动用于合并应用委托中的更改的代码。

    所以,在application:didFinishLaunchingWithOptions: 内注册通知。

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 
    [nc addObserver:self
           selector:@selector(mergeChanges:) 
               name:NSManagedObjectContextDidSaveNotification
             object:nil]; // set nil here
    

    然后,始终在应用程序委托中创建您的 mergesChanges: 方法。这里你需要确保通知在主线程上运行,并且你收到通知的上下文与主线程不同。

    - (void)mergeChanges:(NSNotification *)notification
    {
        if ([notification object] == [self managedObjectContext])
            return;
    
        if (![NSThread isMainThread]) {
            [self performSelectorOnMainThread:@selector(mergeChanges:) withObject:notification waitUntilDone:YES];
            return;
        }
    
        NSManagedObjectContext *mainContext = [self managedObjectContext];
    
        // Merge changes into the main context on the main thread
        [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
                                  withObject:notification
                               waitUntilDone:YES];  
    }
    

    附:在这里,您使用的是非并发 NSOperation,如果插入到 NSOperationQueue 中,它将以并发方式运行(队列将通过 GCD 为您管理)。

    【讨论】:

    • 谢谢你的回答,所以我不需要像@Joel 那样改变我的 nsoperation 伤心吗?覆盖 start 方法,而不是像我那样覆盖 main 方法?
    • 如果您使用NSOperationQueue,则不需要。 @Piero 也看看importing-and-displaying-large-data-sets-in-core-data。在这种情况下,使用并发操作(通过覆盖 start 方法实现)没有用处。
    【解决方案2】:

    这不是并发操作。你的mergeChanges 通知可能永远不会被调用,因为一旦主方法完成,操作就会被释放(这可能会在NSManagedObjectContextDidSaveNotification 选择器被执行之前)。

    如果您想要一个在您完成之前一直存在的并发操作,您需要覆盖start 方法和isConcurrent 方法。您可能会收到此错误,因为您的 NSError 或选择器在完成之前超出范围。我建议阅读这篇文章以了解并发操作与非并发操作的工作原理:

    请查看 Dave Dribin 的 Concurrent Operations Demystified 了解更多详情。

    【讨论】:

    • 感谢您的回答,我必须注册此通知 [nc addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];并插入此方法,或者我不再需要它?
    • 这里不需要创建并发操作。我认为@Piero 使用添加到队列中的操作。
    • 如果您希望并发操作工作,那么您需要进行我建议的更改,以便在收到通知之前操作不会终止。您还可以按照@flexaddicted 的建议将选择器移出操作并将其分流到主线程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 2022-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多