如何解决由于未捕获的异常导致的终止应用程序' RLMException',原因:' Realm从错误的线程访问。'?

时间:2018-02-03 06:13:07

标签: swift search realm rx-swift reactiveui

如何解决此问题Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread.',我花了两天时间寻找此问题的解决方案。我希望有人可以帮助我。我尝试调试并跟踪它,我发现它成功进入.subscribe(onNext:)并读取controller?.tableView.reloadData()但是如果我继续执行程序,它将终止并在控制台日志中显示错误。

        searchBar
            .rx.text
            .orEmpty
            .debounce(0.5, scheduler: MainScheduler.instance)
            .distinctUntilChanged()
            .filter { !$0.isEmpty }
            .map({ [weak controller = self] query -> (tcb: [TrialCourtBranches], query: String)? in
                switch controller?.tcbArray {
                case .some(let array):
                    return (array, query)
                case .none:
                    return nil
                }
            })
            .observeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated)) // change thread before filtering
            .map({ (query) -> [TrialCourtBranches] in // filter and return the result of filtering
                let realm = try! Realm()
                let tcb = realm.objects(TrialCourtBranches.self)
                let tcb_safe = ThreadSafeReference(to: tcb)
                let filtered = realm.resolve(tcb_safe)
                let tcb_filtered:[TrialCourtBranches] = (filtered?.filter({ $0.branch_name.lowercased().contains(searchText.lowercased()) }))!
                return tcb_filtered
            })
            .observeOn(MainScheduler.instance) // go back to main thread
            .subscribe(onNext: { [weak controller = self] (filtered) in
                tcb_filteredArray = filtered
                controller?.tableView.reloadData()
            })
            .disposed(by: bag)

2 个答案:

答案 0 :(得分:0)

Please note I'm not really an IOS dev so this code could easily end up syntactically incorrect, but the issue is that this just isn't a good way to filter in Realm.

You could for example use RxRealm from https://github.com/RxSwiftCommunity/RxRealm which allows you to expose Results as a Changeset Observable.

Then you can switchMap (or in your language, flatMapLatest) on it.

So the end result would be something like this

extension UITableView {
  func applyChangeset(_ changes: RealmChangeset) {
    beginUpdates()
    deleteRows(at: changes.deleted.map { IndexPath(row: $0, section: 0) }, with: .automatic)
    insertRows(at: changes.inserted.map { IndexPath(row: $0, section: 0) }, with: .automatic)
    reloadRows(at: changes.updated.map { IndexPath(row: $0, section: 0) }, with: .automatic)
    endUpdates()
  }
}

And

    searchBar
        .rx.text
        .orEmpty
        .debounce(0.5, scheduler: MainScheduler.instance)
        .distinctUntilChanged()
        .filter { !$0.isEmpty }
        .map({ [weak controller = self] query -> (tcb: [TrialCourtBranches], query: String)? in
            switch controller?.tcbArray {
            case .some(let array):
                return (array, query)
            case .none:
                return nil
            }
        })
        .flatMapLatest({ (query) -> [TrialCourtBranches] in // filter and return the result of filtering
             let realm = try! Realm()
             let results = realm.objects(TrialCourtBranches.self)
                      .filtered($0.branch_name.lowercased()
                      .contains(searchText.lowercased())) 
             return Observable.changeset(from: results)
        })
        .subscribe(onNext: {[unowned self] results, changes in
            if let changes = changes {
                controller?.tableView.applyChangeset(changes)
            } else {
                controller?.tableView.reloadData()
            }
        })
        .disposed(by: bag)

答案 1 :(得分:0)

正确输出

[HttpGet]
public async Task<IStore> GetStore(int id)
{
    IStore result = await ShopExpressions.Store(GenericExpressions.ClientAccess(this.Worker.GetRepo<Store>().DbSet))
        .SingleAsync(str => str.Id.Equals(id));

    this.Worker.ValidateClientAccess(result);

    return result;
}

[HttpGet]
public async Task<IStore> GetStoreLite(int id)
{
    IStore result = await ShopExpressions.StoreLite(GenericExpressions.ClientAccess(this.Worker.GetRepo<Store>().DbSet))
        .SingleAsync(str => str.Id.Equals(id));

    this.Worker.ValidateClientAccess(result);

    return result;
}

[HttpGet]
public async Task<IPage> GetPage(int id)
{
    IPage result = await ShopExpressions.Page(this.Worker.GetRepo<Page>().DbSet)
        .SingleAsync(page => page.Id.Equals(id));

    return result;
}