我有一个名为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
对象?
答案 0 :(得分:1)
引用Realm关于Passing Instances Across Threads的文档:
Realm
,Results
或List
的实例或Object
的托管实例是线程限制的,这意味着它们只能在其上的线程上使用它们被创建了,否则会抛出异常。
在您的情况下,self.trip
是Object
子类的托管实例,您似乎是从主线程访问它,并且在您在{中创建的串行调度队列中重复访问它{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
等托管实例。