使用Xcode-8.2.1,Swift-3.0.2,RealmSwift-2.2.0,iOS-Simulator-10:
我尝试使用Realm来应用MVVM模式(explained by Steve Scott here)。
直到我尝试访问viewmodel-property的那一刻(在VIEW部分内部 - 见下文),一切正常。它说:Realm accessed from incorrect thread
我怎么能让MVVM模式完成分离模型,视图模型和视图的工作,但同时又能获得与领域的线程安全性?
有没有办法让Realm-results(即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)
}
}
}
VIEW-MODEL:
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])
}
}
}
查看:
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 :(得分:2)
您可以使用ThreadSafeReference
将Realm的线程限制类型(Object
,Results
,List
,LinkingObjects
)传递给其他线程。关于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
。
答案 1 :(得分:1)
我找到了解决方法(见下文):也许你有更好的解决方案 - 请告诉我!
这是我的github-code realm_mvvm_c on github
在引入新协议并使(几乎所有东西)符合它之后,事情就成功了。
以下是名为DataEntry的协议:
import Foundation
protocol DataEntry: class {
var idx: Int { get set }
var category: String { get set }
}
现在,让一切都符合它,例如
- &GT;领域对象(即class BalancesDataEntry: Object, DataEntry {...
)
- &GT; getEntries返回值(即func getEntries(_ completionHandler: @escaping (_ entries: [DataEntry]) -> Void)
)
- &GT; View-Model的条目(即fileprivate var entries: [DataEntry]? {..
)
- &GT;所有相应的Model-和View-Model协议也需要DataEntry数据类型(参见git-repo获取完整图片)
之后,只需将MODEL方法getEntries(..)
的completion-handler返回数组更改为新创建的对象实例(即DataEntryDub),该对象实例符合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)
}
}