具有单例的领域线程安全对象

TRG*_*TRG 2 multithreading thread-safety realm swift3

所以我有一个单例类来管理登录到我的应用程序的当前用户.

看起来像这样

class SessionManager {
    static let shared = SessionManager()

    var loggedInUser: User?
}
Run Code Online (Sandbox Code Playgroud)

问题是,如果我想从后台线程获取该用户对象,我不能因为我得到以下异常:

'RLMException', reason: 'Realm accessed from incorrect thread.'
Run Code Online (Sandbox Code Playgroud)

经过一些谷歌搜索后,我找到了线程安全参考.所以我改变了我的课程,改为:

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!)
            }

        }

    }
}
Run Code Online (Sandbox Code Playgroud)

我的想法是我内部持有一个线程安全的引用,然后每当我获取用户对象时,我解析该对象.但是这不起作用,因为您只能解析一次ThreadSafeReference:

'RLMException', reason: 'Can only resolve a thread safe reference once.'
Run Code Online (Sandbox Code Playgroud)

我也试过在getter中解析它一次,但是如果你将解析后的对象从不同的线程引用到它被解析的位置,你就会遇到第一个问题.

目前我可以想到两个选择:

  1. 我为每个线程保留一个引用,并传回正确的线程用户对象(不是100%确定我将如何做到这一点,此刻只是一个理论)

  2. 我可以让我的set/get dispatch到主线程来同步获取对象.所以我总是从主线程中抓住它.我可能无法对对象进行更改,但也许我可以阅读?不确定这实际上是否合情合理.

考虑到问题的普遍性,我100%肯定这是一个之前已经解决过的问题.

同样的领域用户,我该如何解决这个问题呢?

编辑:问题已解决,感谢David在下面.对于SO用户来说,这就是我最后所做的.

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
            }

        }

    }
}
Run Code Online (Sandbox Code Playgroud)

它可能需要一些清理(可以肯定地写出更好的零检查),但这是我采取的基本方法.

这允许我loggedInUser像处理普通对象一样处理属性,每当我读取它时,它从领域(线程安全)抓取一个新的引用,如果我想设置一个新用户(即登录后),我可以loggedInUser = newUser和setter处理如何坚持它.

Dáv*_*tor 9

您的第一个选项肯定是不正确的,因为ThreadSafeReference它是一次性使用参考,如下所述.您的第二个选项可能有效,但只要Realm您尝试访问它,就可以更轻松地创建对实例的新引用,请参阅下面的说明.

您不应该存储对单例Realm实例的引用,因为您无法确保始终从同一个线程访问它.正如Realm文档所述,

的实例Realm,Results或者List,托管实例或者Object是线程限制,这意味着它们只能在创建它们的线程上使用,否则会抛出异常.

这意味着您永远不应该尝试使用let realm = try! Realm()创建单例Realm实例,而不是试图从您的应用程序访问它.

您可以尝试使用ThreadSafeReference创建Realm可以在线程之间安全共享的对象的引用,但是作为文档中线程传递实例的一部分,这些引用只能使用一次,因此它们不能作为单例重用.

但是,不需要这样的事情,因为每次调用时let realm = try! Realm(),框架都会自动创建对您当前使用Realm的线程的引用,只要您不离开该线程,就可以安全地与它进行交互.

用最好的方式Realm在一个线程安全的方式是创建一个新的参考你要Realm使用let realm = try! Realm()每次线程之间移动,并需要访问你的时间Realm.这样您就可以确保永远不会得到错误的线程异常.