ios - Core Data "The Database appears corrupt"——是什么导致了这个错误?

标签 ios objective-c sqlite core-data nsmanagedobjectcontext

我在这里撞墙了,我正在为 SQLLite 数据库使用核心数据,并且能够成功保存到数据库(我已经在离线 SQLLite 浏览器中检查了内容),但是保存第一个查询后,我尝试运行并返回以下错误,我在互联网上找不到与此特定错误相关的任何有用信息:

Core Data: error: -executeRequest: encountered exception = The database appears corrupt. (invalid primary key) with userInfo = { NSFilePath = "/Users/user/Library/Application Support/iPhone Simulator/7.0.3/Documents/db.sqlite";

这里的问题是导致此错误的原因,因为我找不到任何相关信息。

关于一些背景知识,这是我的设置,请假设我对所做的设计有充分的理由并且不要提供“更改你的设计”的答案,除非你能看到一些根本上与模式本身。

我有 3 个托管对象上下文,它们都是 NSPrivateQueueConcurrencyType,第一个 (A) 附加到 Persistent Store Coordinator,第二个 (B) 将 A 设置为其父上下文,第三个 (C) 将 B设置为它的父上下文——一个链。这样做的原因是C是一个可写上下文,从网络源中获取数据并同步并保存,B是UI元素共享的上下文,我希望它是响应式的,最后A是设计的后台上下文卸载上下文 B 和 C 中保存到磁盘的任何延迟

PSC <-- A <-- B <-- C

如果我去掉最后一步(将 A 保存到 PSC),那么应用程序运行良好,将所有内容保存在内存中并查询内存上下文。崩溃仅在我重新添加保存步骤后发生,并且仅在保存后对数据库运行的第一个查询上发生。我的保存和获取执行都包含在 performBlock 中:

这是最后一次保存:

- (void)deepSave
{
    // Save to the Save Context which happens in memory, so the actual write to disk operation occurs on background thread
    // Expects to be called with performBlock

    NSError *error = nil;
    [super save:&error];
    NSAssert(!error, error.localizedDescription);

    // Trigger the save context to save to disk, operation will be queued and free up read only context
    NSManagedObjectContext *saveContext = self.parentContext;
    [saveContext performBlock:^{
        NSError *error = nil;
        [saveContext save:&error];
        NSAssert(!error, error.localizedDescription);
    }];
}

这是执行堆栈(在 NSManagedObjectContext 队列线程上)

#0  0x0079588a in objc_exception_throw ()
#1  0x079d98e7 in -[NSSQLiteConnection handleCorruptedDB:] ()
#2  0x078d9b8d in -[NSSQLiteConnection fetchResultSet:usingFetchPlan:] ()
#3  0x078e24a5 in newFetchedRowsForFetchPlan_MT ()
#4  0x078cd48e in -[NSSQLCore newRowsForFetchPlan:] ()
#5  0x078cca8d in -[NSSQLCore objectsForFetchRequest:inContext:] ()
#6  0x078cc53f in -[NSSQLCore executeRequest:withContext:error:] ()
#7  0x078cbf62 in -[NSPersistentStoreCoordinator executeRequest:withContext:error:] ()
#8  0x078c96c6 in -[NSManagedObjectContext executeFetchRequest:error:] ()
#9  0x0791e526 in -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:] ()
#10 0x0799c1f4 in __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke ()
#11 0x0791e321 in internalBlockToNSManagedObjectContextPerform ()
#12 0x013c34b0 in _dispatch_client_callout ()
#13 0x013b0778 in _dispatch_barrier_sync_f_invoke ()
#14 0x013b0422 in dispatch_barrier_sync_f ()
#15 0x0791e2a2 in _perform ()
#16 0x0791e14e in -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] ()
#17 0x078c96c6 in -[NSManagedObjectContext executeFetchRequest:error:] ()
#18 0x0791e526 in -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:] ()
#19 0x0799c1f4 in __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke ()
#20 0x0791e321 in internalBlockToNSManagedObjectContextPerform ()
#21 0x013c34b0 in _dispatch_client_callout ()
#22 0x013b0778 in _dispatch_barrier_sync_f_invoke ()
#23 0x013b0422 in dispatch_barrier_sync_f ()
#24 0x0791e2a2 in _perform ()
#25 0x0791e14e in -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] ()
#26 0x078c96c6 in -[NSManagedObjectContext executeFetchRequest:error:] ()

最佳答案

好的,我已经找到了。似乎在此设置中 propertiesToFetch 中针对 NSManagedObject resultType(不应该使用,我们的错误)存在一些问题——针对具有父上下文而不是持久协调器的上下文。这个单元测试表明,您所要做的就是设置一个要获取的属性来获取此错误(在没有要获取的属性的情况下进行查询时可以正常工作)。我们的解决方法是停止错误地使用属性来获取 :)

- (void)testManagedObjectContextDefect
{        
    NSManagedObjectContext *contextA = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    contextA.persistentStoreCoordinator = sqllitePersistentStoreCoordinator;
    NSManagedObjectContext *contextB = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    contextB.parentContext = contextA;

    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"GCSCObject" inManagedObjectContext:contextB];

    [contextB performBlockAndWait:^{
        GCSCObject *object = [[GCSCObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:contextB];
        object.serverID = @"1";
        NSError *error = nil;
        XCTAssert([contextB save:&error] && !error, @"Failed to save - %@",error); // B -> A save
    }];

    [contextA performBlock:^{
        NSError *error = nil;
        XCTAssert([contextA save:&error] && !error, @"Failed to save - %@",error); // A -> PSC, background save
    }];

    [contextB performBlockAndWait:^{
        NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"GCSCObject"];
        NSError *error = nil;
        NSArray *results = [contextB executeFetchRequest:request error:&error];
        XCTAssert(results.count == 1 && !error, @"Fetch failed to retrieve - %@ / %@",results,error);
        GCSCObject *object = results[0];
        XCTAssert([object.serverID isEqualToString:@"1"], @"Value retrieval failed");

        // Everything passes up to here, so far so good!

        request = [[NSFetchRequest alloc] initWithEntityName:@"GCSCObject"];
        request.propertiesToFetch = @[@"serverID"]; // This is the culprit of the index crash
        results = [contextB executeFetchRequest:request error:&error];
        XCTAssert(!error, @"%@", error.localizedDescription); // !!! HERE we have a failure, assert: "Core Data: error: -executeRequest: encountered exception = The database appears corrupt.  (invalid primary key) with userInfo = { NSFilePath = "/path/db.sqlite }";
    }];
}

在这种情况下,GCSCObject 是一个常规实体,serverID 是它的参数之一(无论使用哪个参数,或者它是什么类型,我都尝试了多个。这是我对 serverID 参数的描述用于此测试:

serverID description

无论我们是否为 contextA 保存提供 andWait 都会发生崩溃(尽管这样做会使有一个后台队列用于保存的点无效)

我很乐意提供有关为什么会出现这种情况的反馈,但就目前而言,不使用属性来获取可以使我们的应用程序顺利运行。我正在考虑在这里提交 Apple Bug。

关于ios - Core Data "The Database appears corrupt"——是什么导致了这个错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22057147/

相关文章:

objective-c - 销毁和重建单例 iOS

iOs:在启动时,转到指定的 View Controller

Android Room 增加连接池限制

iPhone : How to get colour of each pixel of an image?

ios - 有没有办法在 iOS 上训练 TensorFlow 模型?

ios - 如何将自定义 HTTP header 字段添加到 HLS 的 AVPlayer 请求

android - SQLite openOrCreateDatabase 未知错误(代码 14): Could not open database

java - SQLite 查询不适用于连接字符串

ios - 在 IOS 中手动设置日期值并使用日期选择器

objective-c - 为什么这个字符串是不可变的?