定期在Realm

时间:2017-08-19 15:03:47

标签: ios swift multithreading realm

我有一个名为Trip的Realm对象。它存储用户移动的数据。

class Trip: Object {
    dynamic var id: Int = 0
    dynamic var startTimestamp: Int64 = 0
    dynamic var endTimestamp: Int64 = 0
    dynamic var distance: Double = 0.0
    dynamic var calories: Double = 0.0
    dynamic var averageSpeed: Double = 0.0
}

在视图控制器中,我保留了一个名为trip的类级变量。

fileprivate var trip: Trip?

每当用户开始旅行时,我会初始化一个Trip对象并将其分配给此变量。

trip = Trip()

在整个用户的动作中,我不断用数据更新这个trip对象。

我需要每1分钟将这些数据保存到Realm数据库。所以我跑了一个计时器。

Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(save()), userInfo: nil, repeats: true)

执行一个函数将该对象保存在后台线程中。

fileprivate func save() {
    do {
        DispatchQueue(label: "RealmBackgroundThread").async {
            autoreleasepool {
                let realm = try! Realm()
                try! realm.write {
                    realm.add(self.trip!, update: true)
                }
            }
        }

    } catch {

    }
}

到此为止,它运作正常。问题出在第一次保存之后,当我再次尝试访问该trip对象时,它会因以下错误而崩溃。

  

libc ++ abi.dylib:以类型为realm的未捕获异常终止:: IncorrectThreadException:从错误的线程访问Realm。

我认为这是因为我打开一个新的Realm来保存此对象在后台线程中。我知道Realm不是线程安全的。

但我不知道如何解决这个问题。保存后如何继续使用相同的trip对象?

1 个答案:

答案 0 :(得分:1)

引用Realm关于Passing Instances Across Threads的文档:

  

RealmResultsList的实例或Object的托管实例是线程限制的,这意味着它们只能在其上的线程上使用它们被创建了,否则会抛出异常。

在您的情况下,self.tripObject子类的托管实例,您似乎是从主线程访问它,并且在您在{中创建的串行调度队列中重复访问它{1}}方法。重要的是要记住,调用save()将导致您的代码在不同的线程上执行,具体取决于Grand Central Dispatch的一时兴起。也就是说连续两次调用如...

DispatchQueue.async

...通常会导致在两个不同的线程上执行工作。

关于Passing Instances Across Threads的Realm的文档包含一个示例,说明如何跨线程传递对象的线程安全引用,并在目标线程的DispatchQueue(label: "RealmBackgroundThread").async { // work } 实例中解析它。

很难在您的案例中提供更具体的建议,因为我不清楚您尝试使用Realm方法做什么以及为什么要这样做在计时器上调用它。托管save()实例(从Realm检索的实例,或已经添加到Realm的实例)只能在写事务中修改,因此需要在写入内执行对Object的任何修改您的self.trip方法打开的交易可用于任何目的。对于使用计时器有意义的模式,您需要将save()保留为非托管对象,此时self.trip将更新save()在Realm文件中具有相同ID的实例。要执行此操作,您需要使用Trip而不是Realm.create(_:value:update:),因为Realm.add(_:update:)不会将非托管实例转换为create等托管实例。