在NSUserDefaults中保存一个元组

7

我正在使用元组来存储类似这样的东西。

var accessLavels: (hasInventoryAccess: Bool, hasPayrolAccess: Bool)
accessLavels = (hasInventoryAccess: true, hasPayrolAccess: false)

现在我想把它保存在 NSUserDefaults 中。
NSUserDefaults.standardUserDefaults().setValue(accessLavels, forKey: "AccessLevelKey")
NSUserDefaults.standardUserDefaults().synchronize()

但我得到了以下错误。

Type '(hasInventoryAccess: Bool, hasPayrolAccess: Bool)' 未遵循协议 'AnyObject'

我该如何解决这个问题?如果不可能,那么欢迎提出保存元组的其他建议。

谢谢。


顺便提一下,在NSUserDefaults上调用synchronize几乎从来不是必要的,而且仅仅为了“以防万一”而调用它并不被苹果推荐。 - Jon Shier
NSUserDefaults仅支持属性列表类型:NSData、NSString、NSNumber、NSDate、NSArray或NSDictionary。如果您想存储任何其他类型的对象,可以将其归档以创建NSData实例。 - Bobby
5个回答

8

我遇到了一个类似的情况,尝试使用NSCoder编码元组。我解决这个问题的方法是手动将元组转换为Dictionary。这不是一个很好的解决方案,因为如果元组发生变化,键需要在多个地方更改。

我的元组中有一个嵌套的枚举,并给它一个基本类型(字符串),从中转换原始值。这需要额外的工作,但幸运的是你只有原始数据。

# SerializeableTuple.swift

typealias AccessTuple = (hasInventoryAccess: Bool, hasPayrolAccess: Bool)
typealias AccessDictionary = [String: Bool]

let InventoryKey = "hasInventoryAccess"
let PayrollKey = "hasPayrollAccess"

func serializeTuple(tuple: AccessTuple) -> AccessDictionary {
    return [
        InventoryKey : tuple.hasInventoryAccess,
        PayrollKey : tuple.hasPayrolAccess
    ]
}

func deserializeDictionary(dictionary: AccessDictionary) -> AccessTuple {
    return AccessTuple(
        dictionary[InventoryKey] as Bool!,
        dictionary[PayrollKey] as Bool!
    )
}

# Encoding / Decoding

var accessLavels: AccessTuple = (hasInventoryAccess: true, hasPayrolAccess: false)

// Writing to defaults
let accessLevelDictionary = serializeTuple(accessLavels)
NSUserDefaults.standardUserDefaults().setObject(accessLevelDictionary, forKey: "AccessLevelKey")

// Reading from defaults
let accessDic = NSUserDefaults.standardUserDefaults().dictionaryForKey("AccessLevelKey") as AccessDictionary
let accessLev = deserializeDictionary(accessDic)

1
好巧妙的技巧!它运行良好。虽然我不得不进行一些微小的更改,例如强制转换,而且我认为你应该将“AccessDictionary”传递给“deserializeDictionary()”方法。我更新了你的代码以反映这些更改。希望没问题。无论如何,感谢你提供这个令人惊叹的解决方案。我希望苹果在即将推出的Swift版本中支持这个功能。 - Isuru
顺便问一下,在使用setObject而不是setValue以及在从NSUserDefaults中保存和检索值时使用dictionaryForKey而不是objectForKey是否有特定的原因? - Isuru
1
正如@LucasHuang所指出的,setValue来自于NSKeyValueCodingsetValue使用id/AnyObject作为其类型,因此在编译时不提供与可用性相同的类型安全性。Dictionary是一个类(Dictionary的实例是一个对象),并且是最合适的类型方法。你可以使用dataForKey方法并将Dictionary转换为NSData,但那只是额外的工作。 - fearmint

1
我有一个包含3个值的元组。 以下代码用于保存元组。基本上,我从元组创建了一个字符串(其中各个组件由逗号分隔)。
let defaultsLoad = NSUserDefaults.standardUserDefaults()
if appSingleton.runwayDestShared != nil
{
   // Creating a String from the tuple
   let runwayDestString = appSingleton.runwayDestShared!.0 + "," + appSingleton.runwayDestShared!.1  + "," + appSingleton.runwayDestShared!.2
   defaultsLoad.setObject(runwayDestString, forKey: "RunwayDest")
}

为了检索元组,我检索了字符串,将其分解成数组,并使用该数组重新创建了元组。
let runwayDestString = defaultsLoad.stringForKey("RunwayDest")
if let runwayDestString = runwayDestString
{
    let runwayDestArray = runwayDestString.componentsSeparatedByString(",")
    appSingleton.runwayDestShared = (runwayDestArray[0],runwayDestArray[1],runwayDestArray[2])
}

1
您可以存储 Bool、Float、Int、Object、Double 或 URL,但不能存储 Tuple。因此,您有两个选择,保存两个仅具有 hasPayrolAccess 和 hasPayrolAccess Bool 值:
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "hasInventoryAccess")
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "hasPayrolAccess")
let hasInventoryAccess  = NSUserDefaults.standardUserDefaults().boolForKey("hasInventoryAccess")
println(hasInventoryAccess)

let hasPayrolAccess  = NSUserDefaults.standardUserDefaults().boolForKey("hasPayrolAccess")
println(hasPayrolAccess)

或者使用一个布尔数组进行保存:
var accessLavels = [true,false]
println(accessLavels)
NSUserDefaults.standardUserDefaults().setValue(accessLavels, forKey: "accessLavels")
if let loadAccessLavels = NSUserDefaults.standardUserDefaults().arrayForKey("accessLavels") as? [Bool] {
    if let hasInventoryAccess = loadAccessLavels.first {
        println(hasInventoryAccess)
    }
    if let hasPayrolAccess = loadAccessLavels.last {
        println(hasPayrolAccess)
    }
}

将其存储为两个单独的布尔值可以工作,但不能充分利用元组的强大功能并且会分离数据。将其存储为数组会更好一些,因为只有一个项目被存储,但是使用字典会更清晰,因为有键(特别是对于任意元组)。 - fearmint

0
在文档中,我没有看到任何方法-setValue。根据文档,NSUserDefaults只能保存NSString、NSNumber、NSData、NSDictionary、NSArray、NSData和NSDate。这是链接: NSUserDefaults Class Reference. 因此,在这里您无法保存元组。而-setValue来自NSKeyValueCoding协议。

0

这是一个一年前的问题,但仍然存在:

 let accesLvl : [String:AnyObject] = ["hasInventoryAcces":true, "hasPayrolAccess":false]
 NSUserDefaults.standardUserDefaults().setObject(accesLvl, forKey: "accesLevel")

如果你只保存布尔值,let accesLvl : [String:Bool] 是更好的选择。

如果我不理解某些东西(我对Swift和编程都比较新),使用“元组”与使用字典或结构体相比有什么好处呢?


元组更易于创建和使用。不再需要键。 - Glenn Posadas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接