Realm写操作会阻塞主线程

3
我有一个json文件存储在远程服务器上,我正在使用dataTaskWithURL获取数据(36k条记录)。返回的是JSON(SwiftyJSON),下面是我的完成处理程序。
问题是,一旦我开始进行realm create的操作,所有其他回调就停止执行,直到realm commit发生。我希望这个任务在后台运行并插入数据,让用户继续他们快乐的方式而不会受到影响。
似乎阻塞发生在realm.beginWrite()之后。
RemoteAPI().getMetafile({JSONData, error -> Void in
            if (JSONData != nil) {
                do {
                    print("***** Loading realm")
                    let realm = try Realm()

                    realm.beginWrite()
                    for (_, subJSON) in JSONData {
                        realm.create(Meta.self, value: ["xxxxxx": subJSON["xxxxx"].int!, "xxxxx": subJSON["xxxxx"].stringValue, "xxxxx": subJSON["xxxxx"].stringValue, "xxxxxx": subJSON["xxxxxx"].int!, "xxxxxx": subJSON["xxxxx"].stringValue, "xxxxx": subJSON["xxxxx"].stringValue, "xxxxxx": subJSON["xxxxx"].stringValue], update: true)
                    }

                    try realm.commitWrite()

                    print("***** Finished loading realm")
                } catch _ {}
            } else {
                print("api data fetch failed")
                print(error)
            }
        })

在上面的调用进行时,我有另一个调用:
        RemoteAPI().getLatestActivityData({JSONData, error -> Void in
            if (JSONData != nil) {
                // do stuff

                dispatch_async(dispatch_get_main_queue(), {
                    NSNotificationCenter.defaultCenter().postNotificationName("refreshTableView", object: nil)
                })
            }
        })

这两个调用都来自应用代理。然而,问题在于第二个调用中的观察者要等到上面的第一个调用完成后才会触发。

3个回答

1
如果您不想阻塞主线程,应在后台执行交易。
...

do {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        let realm = try! Realm()

        realm.beginWrite()

        for (_, subJSON) in JSONData {
            realm.create(Meta.self, value: [...], update: true)
        }

        try realm.commitWrite()
    }
}
...

请参阅关于线程的 Realm文档

你的代码出现了错误。无法将可抛出函数转换为非可抛出函数。Realm文档主要讲解跨线程共享数据,而这并不是我在这个特定情况下想要做的。我的应用程序启动时会发生多个远程调用。其中一个调用会在完成时触发观察者。然而,观察者直到Realm写入完成才会触发。 - Sean
我将调度移出了 do/catch,但问题仍然存在。 - Sean
我已经在我的原始问题中添加了一些额外的信息,希望能进一步澄清事情。 - Sean

0
你可以尝试这个:
for (_, subJSON) in JSONData {
    realm.beginWrite()
    realm.create(Meta.self, value: ["xxxxxx": subJSON["xxxxx"].int!, "xxxxx": subJSON["xxxxx"].stringValue, "xxxxx": subJSON["xxxxx"].stringValue, "xxxxxx": subJSON["xxxxxx"].int!, "xxxxxx": subJSON["xxxxx"].stringValue, "xxxxx": subJSON["xxxxx"].stringValue, "xxxxxx": subJSON["xxxxx"].stringValue], update: true)
    try realm.commitWrite()
}

我不知道为什么,但它对我有效。


0
该问题与线程阻塞无关。即使我异步运行请求,但我发现的是,如果我有请求A请求B在异步运行,并且它们都开始尝试进行Realm写入操作,它们基本上会被视为串行请求,并且这些写入操作将排队等待直到初始写入完成。
正如文档中所述(不在线程部分),Realm写入将相互阻塞。他们指出您可以使用线程来解决此问题,但即使使用dispatch_async方法,写入操作仍然会相互阻塞。
然而,我意识到最初的请求只是加载用于应用内搜索的数据,而不是其他任何东西。其余系统使用Realm存储特定于用户的数据。 我的解决方案 请求A代表我的搜索元数据请求填充,为此我创建了一个新的realm文件:
let config = Realm.Configuration(
                 path: utility.getDocumentsDirectory().stringByAppendingPathComponent("Meta.realm"),
                 readOnly: false)

let realm = try! Realm(configuration: config)

请求 B 是将所有用户数据写入并触发了一个表视图刷新,我已经从默认的 realm 中进行了读写。

一旦我实施了这个使用方式的变化,就不再有阻塞问题,一切都能正常工作,符合预期。

老实说,我不知道这是否是 Swift/iOS 的最佳实践(我并非 Swift 开发者),但它确实有效,并且性能相当可接受。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接