realm / realm-swift

Realm is a mobile database: a replacement for Core Data & SQLite
https://realm.io
Apache License 2.0
16.23k stars 2.14k forks source link

realm::IncorrectThreadException: Realm accessed from incorrect thread #3377

Closed borkasm closed 8 years ago

borkasm commented 8 years ago

Goals

I'm facing unexpected exception in a simple case with @Alamofire (see code below)

Description

I want to fetch data for some entity that I want to update in my database. First, I get an instance by its primary key on the db serial queue, then I call the fetch() method for this instance. Response is getting handled on the same queue but when I try to access self.html I get the 'Realm accessed from incorrect thread' exception.

Code Sample

import Cocoa
import RealmSwift
import Alamofire

let dbQueue = dispatch_queue_create("DbQueue", DISPATCH_QUEUE_SERIAL)

class Model: Object {
    dynamic var id = 0
    dynamic var html = ""

    override class func primaryKey() -> String? {
        return "id"
    }

    func fetch() {
        Alamofire.request(.GET, "https://github.com").responseString(queue: dbQueue) { response in
            switch response.result {
            case .Success(let html):
                if self.html != html {
                    let db = try! Realm()
                    try! db.write {
                        self.html = html
                    }
                }
            case .Failure(_):
                break
            }
        }
    }
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(aNotification: NSNotification) {
        // Insert code here to initialize your application
        let db = try! Realm()
        let item = Model()
        try! db.write {
            db.add(item, update:true)
        }

        dispatch_async(dbQueue) {
            let db = try! Realm()
            let dbItem = db.objectForPrimaryKey(Model.self, key: 0)!
            dbItem.fetch()
        }
    }
}

Version of Realm and Tooling

ProductName: Mac OS X ProductVersion: 10.11.4 BuildVersion: 15E65

/Applications/Xcode.app/Contents/Developer Xcode 7.3 Build version 7D175

~/.gem/ruby/2.0.0/bin/pod 0.39.0 Realm (0.98.6) RealmSwift (0.98.6)

/bin/bash GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)

(not in use here)

/usr/bin/git git version 2.6.4 (Apple Git-63)

bdash commented 8 years ago

There is not a one-to-one mapping from dispatch queue to thread. Two blocks that are invoked on a single dispatch queue can be, and often are, executed on different threads. Realm requires that objects be accessed only from the thread they are created, which means that it's not safe to share them between separate blocks dispatched to a given queue. The suggested pattern is to re-fetch your model objects using their primary key in each block that is dispatched.

borkasm commented 8 years ago

Thanks for fast reply. I've already thought of that and indeed I used the pattern you suggest. Will such re-fetching in every block impact on performance?

I think my previous Core Data experience caused this confusion as its contexts are tied to queues but not physical threads.

bdash commented 8 years ago

Refetching based on primary key is cheap, and is unlikely to be a performance problem.