在 iOS 7 中预加载数据库

Posted

技术标签:

【中文标题】在 iOS 7 中预加载数据库【英文标题】:Preloading a database in iOS 7 【发布时间】:2013-11-14 16:07:24 【问题描述】:

过去,我发布的应用程序带有预加载的数据库,因此用户不必在第一次运行时更新它。我在另一个关于 SO 的问题中找到了一些代码(对不起,不再有链接),我添加到我的 App Delegate 的 persistentStoreCoordinator 方法中:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"db.sqlite"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:[storeURL path]])
    
        NSURL *preloadURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"db" ofType:@"sqlite"]];
        NSError* err = nil;

        if (![[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&err])
        
            NSLog (@"Error - Could not preload database.");
        
    

//... more code generated from the template here

当我尝试在 ios 7 中执行此操作时,我没有收到任何错误,但数据库是空的(即使我的 mainBundle 中的数据库包含我期望的所有信息)。我注意到applicationDocumentsDirectory 中有更多的数据库文件(一个 .sqlite-shm 文件和一个 .sqlite-wal 文件)。我还需要对这些文件做些什么吗?还是不再可能在应用程序中提供预加载的数据库?

编辑:我尝试添加代码来复制新的 .sqlite-shm 和 .sqlite-wal 文件,但这没有帮助。

【问题讨论】:

【参考方案1】:

我也复制了 .sqlite-shm 和 .sqlite-wal 文件,并且成功了:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *storePath = [documentsDirectory stringByAppendingPathComponent: @"emm_samples.sqlite"];

// Check if the sqlite store exists
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath]) 
    NSLog(@"File not found... copy from bundle");

    // copy the sqlite files to the store location.
    NSString *bundleStore = [[NSBundle mainBundle] pathForResource:@"emm_samples" ofType:@"sqlite"];
    [[NSFileManager defaultManager] copyItemAtPath:bundleStore toPath:storePath error:nil];

    bundleStore = [[NSBundle mainBundle] pathForResource:@"emm_samples" ofType:@"sqlite-wal"];
    storePath = [documentsDirectory stringByAppendingPathComponent: @"emm_samples.sqlite-wal"];
    [[NSFileManager defaultManager] copyItemAtPath:bundleStore toPath:storePath error:nil];

    bundleStore = [[NSBundle mainBundle] pathForResource:@"emm_samples" ofType:@"sqlite-shm"];
    storePath = [documentsDirectory stringByAppendingPathComponent: @"emm_samples.sqlite-shm"];
    [[NSFileManager defaultManager] copyItemAtPath:bundleStore toPath:storePath error:nil];

else 
    NSLog(@"File exists");

(基于:Core Data Store included in App Bundle)

【讨论】:

【参考方案2】:

iOS 7 中的 Core Data 发生了一些变化,主要是如何执行保存。

引入预写日志 (wal) 是为了提高性能,因此您会看到 WAL sqlite 文件。

您可以告诉您的应用使用旧的“日志模式”:

您可以通过添加 NSSQLitePragmasOption 来指定日志模式 调用时的选项 addPersistentStoreWithType:configuration:url:options:error。例如。到 设置DELETE之前的默认模式:

NSDictionary *options = @ NSSQLitePragmasOption : @@"journal_mode" : @"DELETE" ;

Source

我不确定这是否能解决您的问题,但这是 iOS 7 中 Core Data 发生的重大变化

如果你想了解更多关于 WAL 的知识,我建议观看WWDC session #207, "Whats New In Core Data & iCloud"

【讨论】:

是的,添加 options 字典就可以了。我会查看您链接的视频,希望这不是我最终需要的东西=)。否则我想我将不得不找到另一个解决方案,但这似乎目前可行。 WAL 确实提高了多线程应用程序的性能,因此在更改日志模式时要小心。不过,我不知道如何交付启用 WAL 的预填充数据库。 @DavidCaunt:目前我并不太关心我的应用程序的性能。在我的应用程序的先前版本中,数据库操作非常缓慢,但我显着缩减了许多表以消除浪费的空间,并更改了我的提取以更好地获取我在会话中需要的信息。在这些修改之后,几乎没有可检测到的延迟。如果某些事情发生变化并且性能再次开始受到影响,我会记住再次研究 WAL - 谢谢。 @DavidCaunt:后续 - WWDC 视频提到 WAL 样式可能无法与 NSFileManager 配合使用,这是我用来进行预加载的,所以我猜这是预料之中的行为。 Apple 是否会改变任何东西以使它们在未来兼容,这可能是任何人的猜测。如果他们不这样做,看起来我们需要想出一种全新的方法来让数据库与应用程序一起安装。

以上是关于在 iOS 7 中预加载数据库的主要内容,如果未能解决你的问题,请参考以下文章

NSBundle 中预加载内容的 iOS 存储问题以及来自 API 的增量更新

在 reactjs 组件中预加载数据

如何使用 swift 3 xcode 8 在核心数据中预加载数据库

在linux中预加载动态加载的库

有没有办法在 Interface Builder 中预加载 WKWebView?

在 swift 3 中启动应用程序时,如何在另一个 ViewController 中预加载 UIWebView?