RLMException when trying to perform Realm transaction with Grand Central Dispatch

戏子无情 提交于 2019-12-23 01:42:51

问题


I have these two declared as globals in my AppDelegate

var realmdb: RLMRealm!
var realmQueue = dispatch_queue_create("com.pesto.realmdb", DISPATCH_QUEUE_SERIAL)

In my application(_, didFinishLaunchingWithOptions: _) I set a reference of defaultRealm to my global realmdb like so:

 dispatch_async(realmQueue) {
     realmdb = RLMRealm.defaultRealm()
 }

In another class in my code I have this method:

private func handleChatMessage(message: PSTChatMsg) {

    // check if there is a channel environment for this message
    PSTChatHelpers.getChannelEnvironmentForChannelName(message.channel) { channelEnvironments in

        if channelEnvironments.count == 0 {

            log.verbose("Channel environment for \(message.channel) does not exists. Creating...")

            let newChannelEnvironment = PSTChatHelpers.createChannelEnvironmentFromMessage(message)
            PSTChatHelpers.persistChannelEnvironmentChanges(newChannelEnvironment)

        } else {

            log.verbose("Pushing message to channel environment")

        }

    }
}

and the calling methods are implemented like so:

 class func getChannelEnvironmentForChannelName(channelName: String, withCompletionHandler handler: (results: RLMResults) -> ()) {

    dispatch_async(realmQueue) {

        let predicate = NSPredicate(format: "channelName = %@", channelName)
        var channelEnv = PSTChannelEnvironment.objectsInRealm(realmdb, withPredicate: predicate)

        handler(results: channelEnv)
    }

}

 /**
    Creates a new PSTChannelEnvironment object wth values derived from passed PSTChatMsg

    :param: message a PSTChatMsg to derive channel environment values from
    :returns: The newly created PSTChannelEnvironment object
*/
class func createChannelEnvironmentFromMessage(message: PSTChatMsg) -> PSTChannelEnvironment {

    let channelEnvironment = PSTChannelEnvironment()

    channelEnvironment.channelName = message.channel
    channelEnvironment.associatedPlaceId = message.associatedPlaceId
    channelEnvironment.chattingWithUuid = ""
    channelEnvironment.chattingWithUsername = ""
    channelEnvironment.hasSessionEnded = false
    channelEnvironment.unreadMessages = 0

    return channelEnvironment

}

/**
    Commits the passed PSTChannelEnvironment in Realm database

    :param: The PSTChannelEnvironment to commit to Realm
*/
class func persistChannelEnvironmentChanges(channelEnvironment: PSTChannelEnvironment) {

    dispatch_async(realmQueue) {

        realmdb.beginWriteTransaction()
        realmdb.addObject(channelEnvironment)
        realmdb.commitWriteTransaction()

    }
}

I am getting RLMException, reason: 'Realm accessed from incorrect thread' from the method getChannelEnvironmentForChannelName().

I am a little frustrated on how I can use GCD to handle all my Realm operations so any help would be highly appreciated!


回答1:


As there is in general no fixed association between threads and queues (without the main queue, which only runs on the main thread and vice-versa), you can't just retrieve a RLMRealm instance by dispatching to your queue and cache it. GCD gives no guarantee, that the same queue will be executed by the same thread again.

I'd recommend to refer to your Realm instead by a non-cached factory method:

var realmdb : RLMRealm {
  return RLMRealm.defaultRealm()
}

No worries, the factory method defined on RLMRealm itself implements already a caching strategy, so this operation is not too expensive and you can still cache the instance in a local var per each usage.

If you want to pass it around to decouple classes from each other, you can still write it as a function type:

var realmdb : () -> RLMRealm {
    return { RLMRealm.defaultRealm() }
}



回答2:


You should replace this

dispatch_async(realmQueue) {

    realmdb.beginWriteTransaction()
    realmdb.addObject(channelEnvironment)
    realmdb.commitWriteTransaction()

}

With

dispatch_async(realmQueue) {
    autoreleasepool {
        let realmdb = try! Realm()
        realmdb.beginWriteTransaction()
        realmdb.addObject(channelEnvironment)
        realmdb.commitWriteTransaction()
    }
}

This way you ensure that the Realm instance is closed, and that you create a Realm instance that belongs to that given thread. A Realm instance can only be accessed on the thread where it was created.



来源:https://stackoverflow.com/questions/28646323/rlmexception-when-trying-to-perform-realm-transaction-with-grand-central-dispatc

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!