Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - access to Dictionary of a singleton causes EXC_BAD_ACCESS

Tags:

I have an app managing a simple stocks portfolio. Amongst other things, it keeps a record of the required exchange rates in a dictionary, like so: [ EURUSD=X : 1.267548 ] This disctionary is a Dictionary property of a singleton called CurrencyRateStore.

When updating the stocks quotations, it checks for an updated exchange rate and updates the dictionary with the following code:

CurrencyRateStore.sharedStore()[symbol] = fetchedRate.doubleValue

That calls:

subscript(index: String) -> Double? {
    get {
        return dictionary[index]
    }
    set {
        // FIXME: crashes when getting out of the app (Home button) and then relaunching it
            dictionary[index] = newValue!
            println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
    }
}

The first time the app is started, it works fine. But if I quit the app (with the Home button) and then relaunch it, the currency rates are updated again, but this time, I get a EXC_BAD_ACCESS at the line

dictionary[index] = newValue!

Here is a screenshot: enter image description here

[EDIT] Here is the thread in the debug navigator: enter image description here

I tried to update the dictionary without a subscript, like so:

CurrencyRateStore.sharedStore().dictionary[symbol] = fetchedRate.doubleValue

but without more success. Same if I use the function updateValue:forKey: I didn't have the issue in Objective-C.

Thanks for your help !

[EDIT] Here is the whole class CurrencyRateStore:

class CurrencyRateStore {

// MARK: Singleton
class func sharedStore() -> CurrencyRateStore! {
    struct Static {
        static var instance: CurrencyRateStore?
        static var token: dispatch_once_t = 0
    }

    dispatch_once(&Static.token) {
        Static.instance = CurrencyRateStore()
    }

    return Static.instance!
}

// MARK: Properties

/** Dictionary of currency rates used by the portfolio, presented like  [ EURUSD=X : 1.3624 ] */
var dictionary = [String : Double]()

/** Returns a sorted array of all the keys on the currency rates dictionary */
var allKeys: [String] {
var keysArray = Array(dictionary.keys)
    keysArray.sort {$0 < $1}
    return keysArray
}

init() {
    if let currencyRateDictionary: AnyObject = NSKeyedUnarchiver.unarchiveObjectWithFile(currencyRateArchivePath) {
        dictionary = currencyRateDictionary as [String : Double]
    }
}

subscript(index: String) -> Double? {
    get {
        return dictionary[index]
    }
    set {
        // FIXME: crashes when getting out of the app (Home button) and then relaunching it
        // (ApplicationWillEnterForeground triggers updateStocks)
            dictionary[index] = newValue!
            println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
    }
}


func deleteRateForKey(key: String) {
    dictionary.removeValueForKey(key)
}


/** Removes all currency rates from the Currency rate store */
func deleteAllRates()
{
    dictionary.removeAll()
}


// MARK: Archive items in CurrencyRateStore
var currencyRateArchivePath: String { // Archive path
var documentDirectories: Array = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)

    // Get the only document directory from that list
    let documentDirectory: AnyObject = documentDirectories.first!

    return documentDirectory.stringByAppendingPathComponent("currencyRates.archive")
}

func saveChanges()-> Bool
{
    // return success or failure
    return NSKeyedArchiver.archiveRootObject(dictionary, toFile: currencyRateArchivePath)
}

}
like image 315
Frédéric Adda Avatar asked Oct 22 '14 19:10

Frédéric Adda


1 Answers

This looks to me like a concurrency issue. Swift dictionaries aren't thread safe, and using them from a singleton can lead to multiple reader/writer issues.

Edit: I am pretty sure this is the real answer, based on the given source/debugging dump. To correct what I wrote, specifically MUTABLE dictionaries and arrays (as well as NSMutableDictionary and NSMutableArray) aren't thread safe, and problems arise when using them within Singletons that are accessed from multiple threads, and that appears to be what the sample source code is doing, or enabling other parts of the code to do.

I don't have an Apple link discussing Swift collection class thread safety, but I"m pretty sure common knowledge. But the following tutorial on Grand Central Dispatch discusses the problem in depth and how to solve it using GCD.

http://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1

like image 197
SafeFastExpressive Avatar answered Sep 21 '22 16:09

SafeFastExpressive