masters3d masters3d - 6 months ago 33
Swift Question

NSUserDefaults remove and set object from other thread

NSUserDefaults.standardUserDefaults()
I'd like to understand why after removing an object from a background tread and then adding an object using the same key from main thread, it then becomes impossible to set it again from main. Is this a bug? or does it work as intended?

let mykey = "nsDataKeyOne"
let stringUno:NSString = "................."
let dataOne = stringUno.dataUsingEncoding(NSUTF8StringEncoding)!
let stringDos:NSString = "000000000000000000"
let dataTwo = stringDos.dataUsingEncoding(NSUTF8StringEncoding)!
let userDefaults = NSUserDefaults.standardUserDefaults()


userDefaults.setObject(dataOne, forKey: mykey)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
userDefaults.removeObjectForKey(mykey)
})

userDefaults.setObject(dataOne, forKey: mykey)

print(userDefaults.dataForKey(mykey)) // print nil, why?


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
userDefaults.setObject(dataTwo, forKey: mykey)
print(userDefaults.dataForKey(mykey)) // there is data

})

Answer

Short answer

You are getting nil because the instructions are probably executed in the following order

userDefaults.setObject(dataOne, forKey: mykey)
userDefaults.setObject(dataOne, forKey: mykey)
userDefaults.removeObjectForKey(mykey)
print(userDefaults.dataForKey(mike))

The reason

First of all, NSUserDefaults is thread safe. So we can imagine that a writing operation on a given key is performed when there is not another thread that is changing that value.

When this instruction is executed

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
        userDefaults.removeObjectForKey(mykey)
    })

another thread start trying to add the closure to the global queue. While this thread is waiting to access the global queue the execution of your code does continue so this line is (probably) executed

userDefaults.setObject(dataOne, forKey: mike)

This line does lock NSUserDefaults until dataOne has been saved.

Next the closure is finally in the main queue and it's ready to be executed so

userDefaults.removeObjectForKey(mike)

And finally the print

print(userDefaults.dataForKey(mykey))