2017-07-28 21 views
0

所以我有一個singleton類管理登錄到我的應用程序中的當前用戶。帶單體的領域線程安全對象

它看起來像這樣

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中一次解決它,但你得到的第一個問題,如果你從不同的線程來引用解析對象它在哪裏解決。

目前我能想到的兩個選項:

  1. 我一直爲每個線程參考,並傳回正確的螺紋用戶對象(不是100%肯定我該怎麼做,只是一種理論在的時刻)

  2. 我可以讓我的設置/獲取調度到主線程同步獲取對象。所以我總是從主線抓住它。我可能無法寫入更改對象,但也許我可以閱讀?不知道這實際上是誠實的。

我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和二傳手處理如何堅持它。

回答

5

您的第一個選項絕對不正確,因爲ThreadSafeReference是一次性使用參考,如下所述。您的第二個選項可能會起作用,但您可以在任何時候嘗試訪問它時創建對您的Realm實例的新參考,請參閱下面的說明。

您不應該存儲對單例Realm實例的引用,因爲您無法確保您將始終從同一個線程訪問它。由於Realm文檔狀態,

實例RealmResults,或者List,或Object託管實例是線程限制,這意味着它們只能在創建它們的線程上使用,否則異常被拋出。

這意味着你不應該嘗試使用let realm = try! Realm()創建一個單獨Realm實例,並且不是試圖訪問無處不在,從您的應用程序。

你可以嘗試使用ThreadSafeReference創建到可以線程之間安全地共享Realm對象的引用,但由於文檔狀態的Passing instances across threads一部分,這些引用只能使用一次,所以它們不能被重用爲單身。

然而,沒有必要對這樣的事情,因爲每次調用let realm = try! Realm()時,框架會自動創建一個參考您的當前線程你是讓你只要安全地與它進行交互,你不要在使用Realm不要離開那條線。

在一個線程安全的方式使用Realm最好的方法是創建每次線程之間移動,並需要訪問你的Realm一次使用let realm = try! Realm()新的引用您的Realm。這樣你可以確保你永遠不會得到不正確的線程異常。

+0

所以要確認(我是新太線程,對不起),而不是'var user'屬性,我會更好有一個'var userId'屬性持有對象的主鍵,然後做一個計算屬性getter實例化領域實例並獲取對象,每次我想獲取當前用戶? – TRG

+1

是的,這是正確的。您應該只保留對可以唯一標識您的Realm對象的非Realm對象的引用(主鍵是此的最佳候選對象),以便您可以輕鬆安全地通過線程之間的引用。正如上面所解釋的,我通常只是簡單地創建對領域的新引用,無論我的代碼是否可能更改線程,但計算屬性也可以工作,所以值得一試。 –

+1

謝謝大衛,我會給它一個。 :) – TRG