Chéyo
Chéyo

Reputation: 9367

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

    })

Upvotes: 6

Views: 4409

Answers (2)

Luca Angeletti
Luca Angeletti

Reputation: 59506

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))

Upvotes: 6

Ozgur Vatansever
Ozgur Vatansever

Reputation: 52163

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.

Upvotes: 2

Related Questions