简体   繁体   中英

Can we generate Realm results in background queue and use it on main thread

I'm starting using Realm recently, I'm not sure if my use case is valid:

Normally, when reading a lot of data from DB, I want to put it in a background queue so it will async get the data and later use it on main thread.

For example, I want to fetch several results based on city:

    private var results: [Results<SomeObject>?] = []
    autoreleasepool {
        DispatchQueue(label: "background").async {
            [unowned self] in
            do
            {
                let realm = try Realm()
                for i in 1...City.count
                {
                    self.results.append(realm.objects(SomeObject.self).filter("city=\(i)"))
                }
            }
            catch
            {
                NSLog("Failed to open Realm instance on background qeueue")
            }
        }
    }

And later use results to update my chart:

cell.setChartData(ChartDataFactory.createCombinedData(from: results[0]))

However if I apply this model for Realm, I'm getting error like

Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread.

I understand I must use realm for each thread, and I can do this by reading realm on main thread, but I don't want the realm query block my main thread.

is there any way I can achieve my goal? eg reading realm in a background queue and access the results from another thread, while keeping the auto-refresh feature.

Thanks.

Realm has built-in functionality for running a query on a background thread and delivering the results to the main thread by using Results.observe() .

If you specifically need to perform expensive filtering logic that can't be expressed as a Realm query, you can manually pass an array of objects between threads using ThreadSafeReference .

As of 5.0, you can now construct the query on a background thread and receive notifications on the main thread using the on: parameter to observe() :

DispatchQueue.global().async {
    let realm = try! Realm()
    let results = realm.objects(ObjectType.self).filter("property in %@", expensiveFunction(realm))
    self.token = results.observe(on: .main) { change in
        // do stuff with the results on the main thread
    }
}

Realm objects are only accessible through the realm from which they are fetched or created. Realm instances cannot be shared between threads (which you are aware of), and sharing an object from a specific realm instance to another thread, implicitly has the same effects as sharing a realm instance between threads. This is due to the tight coupling between the objects and the realm instance.

As mentioned in this GitHub issue https://github.com/realm/realm-cocoa/issues/946 , the recommended practice is to share the primary keys (if your realm object overrides the primaryKey method of RealmObject (Objective-C) / Object (Swift)).

You're trying to directly access 'results' property from a different queue and that will crash. You should instead use ThreadSafeReference as indicated on the answer of Thomas.

Make sure to create a ThreadSafeReference for results and call realm.resolve() on your background queue before fetching from your Realm database.

I solved it like this. I see an overall performance improvement, but I couldn't find any implementation example for querying on a background thread. Might be there is a solution with even better performance.

self.results = self.realm.objects(Object.self).filter(predicate).sorted(by: sortProperties)
self.notificationToken = self.results.observe({ (notification) in
    self.tableview.reloadData()
})

This is the results on an iPhone X with a database of ~171k items. Durations are in seconds.

Search on UI tread:

UI thread blocked 0.730504035949707

Search with the code from above:

UI thread blocked 0.28138411045074463
background search duration 0.5073530673980713

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM