问题
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