Swift - 访问单例的Dictionary导致EXC_BAD_ACCESS

时间:2014-10-22 19:54:32

标签: swift dictionary ios8

我有一个管理简单股票投资组合的应用程序。除此之外,它还保留了字典中所需汇率的记录,如下所示: [EURUSD = X:1.267548] 这个disctionary是一个名为CurrencyRateStore的单例的Dictionary属性。

更新股票报价时,会检查更新的汇率并使用以下代码更新字典:

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

那叫:

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!)")
    }
}

第一次启动应用时,它运行正常。 但是,如果我退出应用程序(使用“主页”按钮)然后重新启动它,则会再次更新货币汇率,但这一次,我在行处获得了EXC_BAD_ACCESS

dictionary[index] = newValue!

以下是截图: enter image description here

[编辑]以下是调试导航器中的线程: enter image description here

我尝试更新没有下标的字典,如下所示:

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

但没有更多的成功。如果我使用函数updateValue:forKey,则相同: 我在Objective-C中没有遇到这个问题。

感谢您的帮助!

[编辑]以下是全班 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)
}

}

3 个答案:

答案 0 :(得分:5)

这对我来说就像一个并发问题。 Swift词典不是线程安全的,从单例中使用它们会导致多个读者/作者问题。

编辑:我很确定这是真正的答案,基于给定的源/调试转储。为了纠正我写的内容,特别是MUTABLE词典和数组(以及NSMutableDictionary和NSMutableArray)不是线程安全的,并且在从多个线程访问的单例中使用它们时会出现问题,这看起来就是样本源代码正在执行,或启用代码的其他部分。

我没有Apple链接讨论Swift集合类线程安全性,但我非常清楚常识。但是以下关于Grand Central Dispatch的教程深入讨论了问题,以及如何使用GCD解决它。

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

答案 1 :(得分:1)

错误和行本身:

dictionary[index] = newValue!

让我觉得问题是newValuenil - 错误是由强制解包造成的。 我建议设置一个断点并检查它的值,否则在添加到dict之前打印它。

此外,使用可选绑定保护该语句不是一个坏主意:

if let value = newValue {
    dictionary[index] = value
}

因为如果值类型是可选的,则它可以是nil。

答案 2 :(得分:1)

最后,我联系了Apple技术支持部门。 他们无法重现这个问题。

我认为也许我不需要保存货币汇率,因为在报价更新期间,该功能将检查它所需的货币汇率,并根据需要重新填充字典。 所以我停用了我创建的方法来保存CurrencyRateStore并使用NSKeyedUnarchiver重新加载它。 显然,崩溃消失了!