【问题标题】:Crash when using NSReadOnlyPersistentStoreOption使用 NSReadOnlyPersistentStoreOption 时崩溃
【发布时间】:2014-04-30 04:38:30
【问题描述】:

我正在为我的应用程序使用位于 MainBundle 中的只读 sqlite 数据库。在 PersistentStoreCoordinator 中,我正在使用以下代码加载数据库:

    NSDictionary *storeOptions = @{NSReadOnlyPersistentStoreOption : [NSNumber numberWithBool:YES]};

NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self applicationManagedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[[NSBundle mainBundle] URLForResource:@"applications" withExtension:@"sqlite"] options:nil error:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error description]);
    abort();
}

在 iOS7 中,此代码在模拟器和设备中都崩溃,并出现以下错误:

 CoreData: error: (14) I/O error for database at /var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/myapp.app/applications.sqlite.  SQLite error code:14, 'unable to open database file'
2014-03-22 20:45:34.346 GfxHotkeys3[1369:60b] CoreData: error: Encountered exception I/O error for database at /var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/myapp.app/applications.sqlite.  SQLite error code:14, 'unable to open database file' with userInfo {
    NSFilePath = "/var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/myapp.app/applications.sqlite";
    NSSQLiteErrorDomain = 14;
} while checking table name from store: <NSSQLiteConnection: 0x155b0fd0>
2014-03-22 20:45:34.372 GfxHotkeys3[1369:60b] Unresolved error Error Domain=NSCocoaErrorDomain Code=256 "The operation couldn’t be completed. (Cocoa error 256.)" UserInfo=0x155b01b0 {NSUnderlyingException=I/O error for database at /var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/myapp.app/applications.sqlite.  SQLite error code:14, 'unable to open database file', NSSQLiteErrorDomain=14}, Error Domain=NSCocoaErrorDomain Code=256 "The operation couldn’t be completed. (Cocoa error 256.)" UserInfo=0x155b01b0 {NSUnderlyingException=I/O error for database at /var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/GfxHotkeys3.app/applications.sqlite.  SQLite error code:14, 'unable to open database file', NSSQLiteErrorDomain=14}

在商店选项中传递 nil,将在模拟器中运行应用程序,但不在设备上运行(因为它不能在 MainBundle 上写入)。

我可以通过将数据库复制到文档中并从那里加载来解决这个问题,但我想知道为什么会这样。我多年来一直使用这个选项从 MainBundle 加载只读 sqlite,但在 iOS 7 中崩溃了......

有什么线索吗?

非常感谢

【问题讨论】:

    标签: sqlite core-data ios7


    【解决方案1】:

    要打开一个只读存储,你应该使用

    NSDictionary *storeOptions = @{NSReadOnlyPersistentStoreOption : @YES};
    

    而不是像你那样@NO

    【讨论】:

    • 上例中的错字。感谢您指出。固定以避免混淆。代码在设备上使用 NSReadOnlyPersistentStoreOption @YES 崩溃
    • @Nimrod7: 文件名正确吗?请注意,设备上的文件系统区分大小写,因此“applications.sqlite”和“Applications.sqlite”将是不同的文件。 - 如果您禁用 WAL 模式是否有帮助,如下所述:stackoverflow.com/a/20082157/1187415
    • 文件大小写正确。禁用 WAL 模式没有帮助。在设备和模拟器中都崩溃
    • @Nimrod7:我刚刚找到了这个stackoverflow.com/questions/18773466/…,它看起来与您的问题非常相似(我也给出了错误的答案:-)。请注意,禁用 WAL 模式是那里的解决方案。
    • 问题看起来一样。 iOS 6 的数据库可以工作,所以我想我必须使用 iOS 6 模拟器并禁用 WAL 模式,然后将数据库导入 iOS7 项目。我将其标记为正确答案,这是对正在发生的事情的最完整的解释。感谢您指出这一点。
    【解决方案2】:

    我认为您可以通过首先将商店复制到文档目录来轻松解决问题。然后,您可以传递或不传递只读选项(没关系,您不能写入它)。

    至少这可能是一个很好的测试,看看您是否可以隔离问题..

    我认为崩溃的原因可能是因为新版本的 SQLite 在访问存储时会创建另外两个文件。因为捆绑包不可写,所以您会崩溃。

    【讨论】:

    • 如果我复制到文档,它可以工作。我只是想知道为什么它在以前的 iOS 版本中工作并在 iOS 7 上崩溃
    • 见我上面的解释。
    【解决方案3】:

    问题是原始文件是使用 WAL 选项创建的(现在在 OS X Mavericks 和 iOS 7 上是默认设置)。为了能够使用 NSReadOnlyPersistentStoreOption,您需要创建一个关闭 WAL 的文件。您需要将此指令添加到 iOS 项目中:

    NSDictionary *storeOptions = @{NSReadOnlyPersistentStoreOption : [NSNumber numberWithBool:YES], NSSQLitePragmasOption : @{@"journal_mode" : @"DELETE"}}
    

    以及在您创建原始文件的项目中。 E.i.将您的数据重新导入使用 NSSQLitePragmasOption 设置创建的 NSPersistantStore 文件:

    NSDictionary *storeOptions = @{NSSQLitePragmasOption : @{@"journal_mode" : @"DELETE"}};
    

    我认为这是实现中的一个错误,因为此错误没有技术原因,文件没有加载仅仅是因为 Core Data 处理使用 WAL 创建的文件上的元数据的方式。

    【讨论】:

      【解决方案4】:

      在 swift 3.0 中,使用以下内容初始化持久存储协调器:

      fileprivate lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
          var coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
      
          let bundlePath = Bundle.main.path(forResource: "TrackWaypoints", ofType: ".sqlite")
          let bundleURL = URL(fileURLWithPath: bundlePath!)
      
          let hi = [NSReadOnlyPersistentStoreOption:true, NSSQLitePragmasOption: ["journal_mode": "delete"]] as [String: Any]
          let store = try! coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: bundleURL, options: hi)
          return coordinator
      }()
      

      【讨论】:

        猜你喜欢
        • 2020-12-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-27
        • 2015-07-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多