所以我有一个单例类来管理登录到我的应用程序的当前用户。
看起来像这样
class SessionManager {
static let shared = SessionManager()
var loggedInUser: User?
}
问题在于,如果我想从后台线程获取该用户对象,我就无法获得以下异常:
'RLMException', reason: 'Realm accessed from incorrect thread.'
经过一些谷歌搜索后,我找到了线程安全参考。所以我改变了我的课程,改为:
class SessionManager {
static let shared = SessionManager()
private var threadSafeUser: ThreadSafeReference<User>?
var loggedInUser: User? {
get {
if nil === self.threadSafeUser {
return nil
} else {
let realm = try! Realm()
return realm.resolve(self.threadSafeUser!)!
}
}
set {
if nil === newValue {
self.threadSafeUser = nil
} else {
self.threadSafeUser = ThreadSafeReference(to: newValue!)
}
}
}
}
我的想法是我在内部持有一个线程安全的引用,然后每当我获取用户对象时,我都会解析该对象。但是,这不起作用,因为您只能解析一次ThreadSafeReference:
'RLMException', reason: 'Can only resolve a thread safe reference once.'
我也曾尝试在getter中解析它,但如果你将解析后的对象从不同的线程引用到解析它的位置,你就会遇到第一个问题。
目前我可以想到两个选择:
我为每个线程保留一个引用,并传回正确的线程用户对象(不是100%肯定我会如何做到这一点,只是目前的理论)
我可以让我的set / get dispatch到主线程来同步获取对象。所以我总是从主线程中抓住它。我可能无法对对象进行更改,但也许我可以阅读?不确定这实际上是否合情合理。
我100%确定这是一个之前已经解决的问题,考虑到问题的一般性。
所以同域用户,我应该如何解决这个问题?
编辑:问题已解决,感谢David在下面。对于S.O用户来说,这就是我最后所做的。
class SessionManager {
static let shared = SessionManager()
private var loggedInUserId: Int?
var loggedInUser: User? {
get {
if nil == loggedInUserId {
return nil
}
let realm = try! Realm()
return realm.object(ofType: User.self, forPrimaryKey: loggedInUserId)
}
set {
if nil == newValue {
loggedInUserId = nil
} else {
loggedInUserId = newValue!.id
}
}
}
}
可能需要进行一些清理(可以肯定地写出更好的零检查),但这是我采取的基本方法。
这允许我像对待普通对象一样处理loggedInUser
属性,每当我读取它时,它从领域(线程安全)抓取一个新引用,如果我想设置一个新用户(即登录后),我可以loggedInUser = newUser
,而setter会处理如何保留它。
答案 0 :(得分:8)
您的第一个选项肯定是不正确的,因为ThreadSafeReference
是一次性使用参考,如下所述。您的第二个选项可能有效,但只要您尝试访问它,就可以更轻松地创建对Realm
实例的新引用,请参阅下面的说明。
您不应该存储对单例Realm
实例的引用,因为您无法确保始终从同一个线程访问它。正如Realm
文档所述,
Realm
,Results
或List
的实例或Object
的托管实例是线程限制的,这意味着它们只能在其上的线程上使用它们被创建了,否则会抛出异常。
意味着您永远不应该尝试使用let realm = try! Realm()
来创建单个Realm
实例,而不是尝试从您的应用中访问它。
您可以尝试使用ThreadSafeReference
创建对可以在线程之间安全共享的Realm
个对象的引用,但正如文档的Passing instances across threads部分所述,这些引用只能使用一次,所以他们不能作为单身人士重复使用。
但是,没有必要这样做,因为每次调用let realm = try! Realm()
时,框架会自动创建对您正在使用的线程上当前使用的Realm
的引用,使您能够安全地进行交互只要你不离开那个帖子就可以了。
以线程安全的方式使用Realm
的最佳方法是,每次在线程之间移动并需要访问{时,使用Realm
创建对let realm = try! Realm()
的新引用{1}}。这样您就可以确保永远不会得到不正确的线程异常。