Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

    })
like image 774
Chéyo Avatar asked May 21 '16 23:05

Chéyo


2 Answers

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))
like image 180
Luca Angeletti Avatar answered Sep 20 '22 22:09

Luca Angeletti


No, it is not a bug. It is very likely that userDefaults.removeObjectForKey(mykey) was already executed by the DISPATCH_QUEUE_PRIORITY_HIGH queue right after you'd called setObject(dataOne, forKey: mykey) the second time so it has already been deleted at the time you want to print it out.

If you change the first dispatch_async to dispatch_sync, you'll see the data will be there when you print.

like image 23
Ozgur Vatansever Avatar answered Sep 21 '22 22:09

Ozgur Vatansever