2017-02-18 122 views
2

使用的Xcode-8.2.1,夫特-3.0.2,RealmSwift-2.2.0,IOS的模擬器-10:帶領域的MVVM:跨線程傳遞領域結果?

我嘗試應用使用境界MVVM模式(explained by Steve Scott here)。

一切工作,直到現在(在VIEW部分 - 見下文),我嘗試訪問viewmodel屬性。它說:Realm accessed from incorrect thread

我怎麼能仍然使MVVM模式做它的工作分離模型,視圖模型和視圖,但在同一時間,獲得領域的線程安全?

是否有辦法讓Realm結果(即Results<BalancesDataEntry>)跨線程傳遞?

這裏是我的代碼: (問題發生在最底層,查看部之中)

// REALM-OBJECT: 

import Foundation 
import RealmSwift 

class BalancesDataEntry: Object { 

    dynamic var category: String = "" 
    dynamic var index: Int = 0 
} 

MODEL:

import Foundation 
import RealmSwift 

class MVVMCBalancesModel: BalancesModel 
{ 

    fileprivate var entries = [BalancesDataEntry]() 
    let realm = try! Realm() 

    init() { 
     self.createDataEntries() 
    } 

    fileprivate func createDataEntries() { 

     let myBalance = BalancesDataEntry() 
     myBalance.index = 0 
     myBalance.category = "Love" 

     try! self.realm.write { 

      self.realm.deleteAll() 
      self.realm.add(myBalance) 
     } 
    } 

    func getEntries(_ completionHandler: @escaping (_ entries: [BalancesDataEntry]) -> Void) 
    { 
     // Simulate Aysnchronous data access 
     DispatchQueue.global().async { 

      let realmThread = try! Realm() 
      let returnArray: [BalancesDataEntry] = Array(realmThread.objects(BalancesDataEntry.self)) 
      completionHandler(returnArray) 
     } 
    } 
} 

視圖模型:

import Foundation 
import RealmSwift 

class MVVMCBalancesViewModel: BalancesViewModel 
{ 
    weak var viewDelegate: BalancesViewModelViewDelegate? 
    weak var coordinatorDelegate: BalancesViewModelCoordinatorDelegate? 

    fileprivate var entries: [BalancesDataEntry]? { 
     didSet { 
      viewDelegate?.entriesDidChange(viewModel: self) 
     } 
    } 

    var model: BalancesModel? { 
     didSet { 
      entries = nil; 
      model?.getEntries({ (myEntries) in 
       self.entries = myEntries 
      }) 
     } 
    } 

    var title: String { 
     return "My Balances" 
    } 

    var numberOfEntries: Int { 
     if let entries = entries { 
      return entries.count 
     } 
     return 0 
    } 

    func entryAtIndex(_ index: Int) -> BalancesDataEntry? 
    { 
     if let entries = entries , entries.count > index { 

      return entries[index] 
     } 
     return nil 
    } 

    func useEntryAtIndex(_ index: Int) 
    { 
     if let entries = entries, let coordinatorDelegate = coordinatorDelegate , index < entries.count { 
      coordinatorDelegate.balancesViewModelDidSelectData(self, data: entries[index]) 
     } 
    } 
} 

VIEW:

import UIKit 

class MVVMCBalancesViewController: UIViewController { 

    @IBOutlet weak var label1Outlet: UILabel! 
    @IBOutlet weak var label2Outlet: UILabel! 

    var viewModel: BalancesViewModel? { 
     willSet { 
      viewModel?.viewDelegate = nil 
     } 
     didSet { 
      viewModel?.viewDelegate = self 
      refreshDisplay() 
     } 
    } 

    var isLoaded: Bool = false 

    func refreshDisplay() 
    { 
     if let viewModel = viewModel , isLoaded { 

      // !!!!!!! HERE IS THE ISSUE: Realm accessed from incorrect thread !!!! 
      self.label1Outlet.text = viewModel.entryAtIndex(0)?.category 
      self.label2Outlet.text = viewModel.entryAtIndex(1)?.category 
     } else { 

     } 
    } 

    override func viewDidLoad() 
    { 
     super.viewDidLoad() 

     isLoaded = true 
     refreshDisplay(); 
    } 

} 

extension MVVMCBalancesViewController: BalancesViewModelViewDelegate 
{ 
    func entriesDidChange(viewModel: BalancesViewModel) 
    { 

    } 
} 
+0

看來問題在於你每次都要從Realm中複製數據來強制執行異步,這是非常不必要的,因爲查詢結果已經被異步更新。您可以只進行一次查詢並將結果對象存儲在模型對象中。然後你可以直接從'getEntries()'方法返回它,它總是最新的。 – ast

回答

1

我找到了一個解決方法(見下面):也許你有更好的解決方案 - 請讓我知道!

這是我github-code realm_mvvm_c on github

引入一個新的協議,使(幾乎所有)符合它之後,事情成功的。

這裏是稱爲的DataEntry的協議:

import Foundation 

protocol DataEntry: class { 

    var idx: Int { get set } 
    var category: String { get set } 
} 

現在,使一切符合它,如

- >領域對象(即class BalancesDataEntry: Object, DataEntry {...

- >的getEntries返回值(即func getEntries(_ completionHandler: @escaping (_ entries: [DataEntry]) -> Void)

- > View-Model的條目(即fileprivate var entries: [DataEntry]? {..

- >所有相應的機型和視圖模型的協議還需要的DataEntry數據類型(請查看git回購的完整畫面)

之後,這是足以改變完成處理程序返回數組MODEL的方法getEntries(..)到新創建的對象實例(即。 DataEntryDub)是keept符合的DataEntry協議:

func getEntries(_ completionHandler: @escaping (_ entries: [DataEntry]) -> Void) 
{ 
    // Simulate Aysnchronous data access 
    DispatchQueue.global().async { 

     let realmThread = try! Realm() 

     class DataEntryDub: DataEntry { 

      var idx: Int 
      var category: String 

      init(idx: Int, category: String) { 
       self.idx = idx 
       self.category = category 
      } 
     } 

     var returnArray = [DataEntry]() 
     for entry in realmThread.objects(BalancesDataEntry.self) { 
      returnArray.append(DataEntryDub(idx: entry.idx, category: entry.category)) 
     } 
     completionHandler(returnArray) 
    } 
} 

這是我github-code realm_mvvm_c on github

2

您可以使用ThreadSafeReference傳遞領域的線程限制類型(ObjectResultsListLinkingObjects),以不同的線程。該文檔的上Passing Instances Across Threads部分包含跨線程傳遞一個Object子類的單個實例的本實施例中:

let person = Person(name: "Jane") 
try! realm.write { 
    realm.add(person) 
} 
let personRef = ThreadSafeReference(to: person) 
DispatchQueue(label: "background").async { 
    let realm = try! Realm() 
    guard let person = realm.resolve(personRef) else { 
    return // person was deleted 
    } 
    try! realm.write { 
    person.name = "Jane Doe" 
    } 
} 

它可類似地用於Results

+0

有趣的概念!我會仔細看看它。非常感謝你。 – iKK