作为 UIBackgroundTask 运行时,后台线程上的核心数据迁移失败

Posted

技术标签:

【中文标题】作为 UIBackgroundTask 运行时,后台线程上的核心数据迁移失败【英文标题】:Core-Data migration on background thread failing when running as a UIBackgroundTask 【发布时间】:2012-01-25 12:05:58 【问题描述】:

我的代码:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
        @autoreleasepool             
            // Now on a background thread

            // Setup background task
            __block UIBackgroundTaskIdentifier bgTask;

            void (^finishBackgroundTask)(void) = ^(void) 
                [[UIApplication sharedApplication] endBackgroundTask:bgTask];
                bgTask = UIBackgroundTaskInvalid;
            ;

            // Start background task
            bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:finishBackgroundTask];

            // The method below migrates a core data database and takes ages
            [MyClass migrateCoreDataStuff];

            finishBackgroundTask();
        
    );

我得到的错误是NSUnderlyingException = "Fatal error. The database at /var/mobile/Applications/55B83D5F-CCF5-438E-BECA-B97DB5505541/Documents/Blah.sqlite is corrupted. SQLite error code:11, 'database disk image is malformed'";

仅当以下全部为真时才会发生迁移错误: * 迁移在后台线程上 * 迁移作为 UIBackgroundTask 运行 * 我在设备上运行,不是模拟器

我正在运行 ios 4.3.5,为 iOS 4.0 构建。

【问题讨论】:

哎呀...想通了。永远不要从主线程以外的任何地方调用 UIKit 中的任何东西。在这种情况下,我正在启动一个后台任务 ([[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:]),从而从后台线程向我的 UIApplication 实例发送消息。这是 __block UIBackgroundTaskIdentifier bgTask; void (^finishBackgroundTask)(void) = ^(void) [[UIApplication sharedApplication] endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; ; bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:finishBackgroundTask];dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ @autoreleasepool [MyClass migrateCoreDataStuff]; dispatch_async(dispatch_get_main_queue(), finishBackgroundTask); ); 这不太可能是根本问题。 beginBackgroundTaskWithExpirationHandler 在文档中被特别标记为线程安全。你可能只是在掩盖真正的问题。 你说得对,上面的代码实际上是行不通的(我有点草率了)。 【参考方案1】:

没有看到migrateCoreDataStuff 的内容,很难看出确切的问题。然而,非主线程上的 Core Data 是一个棘手的问题。阅读http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html。您可能至少需要为新线程提供一个单独的托管对象上下文。

【讨论】:

是的,那个方法很大,我不能在这里发布,但本质上它是从media.pragprog.com/titles/mzcd/code/ProgressiveMigration/… 逐步迁移的(我按照你的建议使用了一个单独的上下文,当 migrateCoreDataStuff 正在运行时,什么都没有应用程序中的 else 可能正在访问持久存储) 简单看一下代码表明您正在共享托管对象上下文?至少,它只在一个地方被创建,并且将来每次调用都会返回该引用?

以上是关于作为 UIBackgroundTask 运行时,后台线程上的核心数据迁移失败的主要内容,如果未能解决你的问题,请参考以下文章

处理 UIBackgroundTask Expiration 正在停止我的 beaconRanging 方法

将 Microsoft Access 作为计划任务运行后退出

如何在 CMake 中构建过程后复制目标的所有运行时依赖项?

使用 Azure SQL 服务器作为后端数据库运行 Apache Airflow

怎么把程序作为服务来运行?

通过BackgroundTask在后台维护多路连接会话?