objective-c - 多线程核心数据 iOS 崩溃

标签 objective-c ios core-data nsmanagedobjectcontext nsoperation

我正在开发一个使用 SQLLite 核心数据数据库的 iOS 应用程序。该应用程序在后台线程中运行一个同步循环,该线程从 Web 服务中获取数据并将其写入数据库。发生这种情况时,前台 (UI) 线程会继续运行,从而允许用户对数据库运行搜索。

我正在对应用程序进行压力测试,但它崩溃了。当后台同步任务运行时,我正在前台对数据库运行搜索。数据库中大约有 10,000 条记录,所以它并不大。

后台线程是使用 NSOperation 创建的,它在 NSOperation 的主要方法中创建了一个 NSManagedObjectContext。

前台线程使用一个不同的 NSManagedObjectContext 对象,该对象在 appDelegate 中初始化(并由其可用)。

后台同步线程一次将一千条记录写入数据库,然后其 managedobjectcontext 执行保存。

NSOperation 的主要方法如下所示:

-(void) main {

    NSDictionary* dictionary = [ HPSJSON getDictionaryFromData:_data ];

    // NEED to create the MOC here and pass to the methods.
    NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] init];
    [moc setUndoManager:nil];
    //[moc setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
    //[moc setMergePolicy:NSOverwriteMergePolicy];
    [moc setPersistentStoreCoordinator:getApp().persistentStoreCoordinator];

    if (dictionary==nil){

        [ getApp().operationManager performSelectorOnMainThread: [ self getFailedFunction ] withObject:nil waitUntilDone:NO ];    
        return;
    }

    NSString* rc = [self processData: dictionary andMOC:moc ]; // Writes lots of records to the db and saves the moc

    // performSelectorOnMainThread invokes a method of the receiver on the main thread using the default mode.
    // i.e. call the method within HPSOperationManager as specified by the getSuccessFunction of the specialised sub-class
    [ getApp().operationManager performSelectorOnMainThread: [ self getSuccessFunction ] withObject:rc waitUntilDone:NO ];        

}

processData 方法(由 main 调用)包含大量代码,但这里是执行保存的代码片段:

@try {
            NSLog(@"HPSDbOperation%@ about to save Indexes moc",objectName);

            if (rcHappy==YES)
            {
                // register for the moc save notification - this is so that other MOCs can be told to merge the changes
                [[NSNotificationCenter defaultCenter] 
                 addObserver:getApp() 
                 selector:@selector(handleDidSaveNotification:)
                 name:NSManagedObjectContextDidSaveNotification 
                 object:moc];

                NSError* error = nil;
                if ([moc save:&error] == YES)
                {
                    NSLog(@"HPSDbOperation%@ Indexes SAVED",objectName);

                }else {
                    NSLog(@"HPSDbOperation%@ Indexes NOT saved ",objectName);
                }

                // unregister from notification
                [[NSNotificationCenter defaultCenter] 
                 removeObserver:getApp() 
                 name:NSManagedObjectContextDidSaveNotification 
                 object:moc];

            }



        }
        @catch (NSException * e) {
            NSLog(@"HPSDbOperationBase save Indexes Exception: %@", e);
            rcHappy=NO;
        }

appDelegate 包含以下方法来处理 ManagedObjectContext 合并:

- (void)handleDidSaveNotification:(NSNotification*) note 
{

    @try {

        NSLog(@"appDelegate handleDidSaveNotification about to run");
        [__managedObjectContext mergeChangesFromContextDidSaveNotification:note];
        NSLog(@"appDelegate handleDidSaveNotification did run");

    }
    @catch (NSException * e) {
        NSLog(@"appDelegate handleDidSaveNotification Exception: %@", e);
    }

}

我让同步运行,然后通过运行连续搜索来强调前台 UI 线程。经过一三分钟的同步和搜索后,应用程序崩溃了。它似乎没有被我的任何 try-catch 构造捕获。 Xcode 中的崩溃日志显示如下:

libobjc.A.dylib`objc_msgSend:
0x34d92f68:  teq.w  r0, #0
0x34d92f6c:  beq    0x34d92faa               ; objc_msgSend + 66
0x34d92f6e:  push.w {r3, r4}
0x34d92f72:  ldr    r4, [r0]  <-- exception here Thread1 EXC_BAD_ACCESS (Code=1)
0x34d92f74:  lsr.w  r9, r1, #2
0x34d92f78:  ldr    r3, [r4, #8]
0x34d92f7a:  add.w  r3, r3, #8
0x34d92f7e:  ldr    r12, [r3, #-8]
0x34d92f82:  and.w  r9, r9, r12
0x34d92f86:  ldr.w  r4, [r3, r9, lsl #2]
0x34d92f8a:  teq.w  r4, #0
0x34d92f8e:  add.w  r9, r9, #1
0x34d92f92:  beq    0x34d92fa6               ; objc_msgSend + 62
0x34d92f94:  ldr.w  r12, [r4]
0x34d92f98:  teq.w  r1, r12
0x34d92f9c:  bne    0x34d9317e               ; objc_msgSendSuper_stret + 34
0x34d92f9e:  ldr.w  r12, [r4, #8]
0x34d92fa2:  pop    {r3, r4}
0x34d92fa4:  bx     r12
0x34d92fa6:  pop    {r3, r4}
0x34d92fa8:  b      0x34d92fb0               ; objc_msgSend_uncached
0x34d92faa:  mov.w  r1, #0
0x34d92fae:  bx     lr

我对 iOS、objective-c 和核心数据还很陌生。我对如何解决这个问题有点困惑。我如何才能准确判断应用程序出错的位置/原因?任何人都知道为什么它可能会崩溃?我已经阅读了一些关于核心数据中的多线程的文章,我相信通过在 NSOperation 的主要方法中创建 MOC,我遵循了指导原则,并且通过使用 handleDidSaveNotification,我也正确地合并了我的 MOC。

帮助!谢谢。

最佳答案

多线程核心数据使用的主要规则是托管对象上下文和持久存储的“线程限制”。这意味着,拥有线程本地托管对象上下文但不将它们从一个线程传递到另一个线程是安全的。如果这样做,您必须自行处理锁定和同步。

看来你是在副线程中创建了一个managed object context,然后传给了主线程。我理解正确吗?如果是这样,这可以解释您崩溃的原因。

我想到的一种可能性是:

  1. 后台线程保存;通知已发送;

  2. 主线程开始合并;

  3. 合并正在进行时,后台线程会进行另一次保存。

不是第 3 点的“保存”,它可以是您在 processData 方法中执行的将 moc 置于不同状态的任何操作。

在我看来,您应该能够通过已有的日志跟踪轻松验证这种情况是否是崩溃的原因。

关于objective-c - 多线程核心数据 iOS 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11676212/

相关文章:

iphone - 如何将 NSNumber 转换为 NSData?

swift - Swift 中使用自定义 TableViewCell 的核心数据图像

ios - Firebase Debug模式不上传日志事件

ios - webview 音频在我的 iPad 上无法正常工作

ios - UICollectionView : How do I implement auto-resizing of UICollectionViewCell in terms of its height?

ios - iPad 应用程序永远不会安装在 Retina 模拟器中

ios - 动画 CAShapeLayer/CAGradientLayer

iphone - 如何从开发人员 iPhone 上的开发人员应用程序检索 Core Data SQL 存储

ios - 如何使用 swift 将 MKPolyline 属性存储为可转换的 IOS 核心数据?

ios - 碰撞检测不起作用?