当用户默认为空时,Swift和NSUserDefaults - EXC_BAD_INSTRUCTION

Lom*_*baX 10 ios swift

我正在将我的一个项目转换为Swift,逐个文件.我对NSUserDefaults有一种奇怪的行为.我使用NSString而不是String来与其他代码兼容

var selectedMonth : NSString {
get {
    return NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value()) as NSString
}
set {
    NSUserDefaults.standardUserDefaults().setObject(self, forKey: AppUserDefaults.SelectedMonth.value())
    NSUserDefaults.standardUserDefaults().synchronize()
}
}
Run Code Online (Sandbox Code Playgroud)

该方法适用于获取/设置属性,但是当第一次执行应用程序并且用户默认值为空时,我收到EXC_BAD_INSTRUCTION错误return NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value()) as NSString

问题不在于回报,因为我可以"扩展"这句话:

    let defaultKey = AppUserDefaults.SelectedMonth.value()
    let defaults = NSUserDefaults.standardUserDefaults()  // defaults is correctly set
    let result : AnyObject = defaults.objectForKey(defaultKey)  // <-- the error is here
    return result as NSString
Run Code Online (Sandbox Code Playgroud)

目标C中的类似语句正常工作,返回nil,但我看到objectForKeyswift 中的声明是

func objectForKey(defaultName: String!) -> AnyObject!
Run Code Online (Sandbox Code Playgroud)

注意最后的感叹号,似乎必须从函数返回一个对象,但我认为正确的声明(使它像objc一样工作)应该以?结尾?所以我认为行为发生了变化,并没有写在文档中.

你认为这是一个错误(所以我会提交)或者我错过了什么?谢谢

Lom*_*baX 12

编辑
这个答案不再更新,因为在最终的Swift版本中Apple更改了NSUserDefaults上的objectForKey的方法声明(以及从目标c桥接的许多其他方法)以返回一个可选的(?)而不再是一个隐式解包的可选(!)
结束编辑

好的,我更好地阅读了文档和含义!在一个声明,这是一个不同于!使用变量时.

!在一个声明中意味着该变量是隐式展开的(在Swift书上,读取"Implicitly Unwrapped Optionals"和"Unownedly References and Implicitly Unwrapped Optional Properties"章节以更好地理解它意味着什么

简单来说,这意味着返回值仍然可以为零,但由于它"很少"发生,因此返回值/变量未标记为可选(使用?).这样做是为了避免程序员每次需要打开值(使用if语句检查并解开值)并使用更干净的代码.

但是,第一个问题是EXC_BAD_INSTRUCTION是在错误的行上给出的,因为这个语句

let defaultKey = AppUserDefaults.SelectedMonth.value()
let defaults = NSUserDefaults.standardUserDefaults() 
let result : AnyObject = defaults.objectForKey(defaultKey) 
return NSString(UTF8String: "")
Run Code Online (Sandbox Code Playgroud)

没有给出错误.(结果没有被返回,但是objectForKey调用没有失败)所以这让我再次走上正确的道路并理解了Implicitly Unwrapped Optionals的含义

解决方案很简单,因为返回的值可以是nil变量selectedMonth应该有一个?(标记为可选)或a!(被标记为隐式展开的可选)

例子

1)返回AnyObject?optinal value作为var selectedMonth的类型

var selectedMonth : AnyObject? {
get {
    return NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value())

}
set {
    NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
    NSUserDefaults.standardUserDefaults().synchronize()
}
}
Run Code Online (Sandbox Code Playgroud)

2)返回一个可选的NSString ?,将返回类型转换为AnyObject?再到NSString?

var selectedMonth : NSString? {
get {
    return NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value()) as AnyObject? as NSString?
}
set {
    NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
    NSUserDefaults.standardUserDefaults().synchronize()
}
}
Run Code Online (Sandbox Code Playgroud)

这就像写作

var selectedMonth : NSString? {
get {
    var returnValue : AnyObject? = NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value())
    return returnValue as NSString?
}
set {
    NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
    NSUserDefaults.standardUserDefaults().synchronize()
}
}
Run Code Online (Sandbox Code Playgroud)

3)返回一个隐式包装的NSString!

var selectedMonth : NSString! {
get {
    var returnValue : AnyObject! = NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value())
    return returnValue as NSString!
}
set {
    NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
    NSUserDefaults.standardUserDefaults().synchronize()
}
}
Run Code Online (Sandbox Code Playgroud)

4)短版

var selectedMonth : NSString! {
get {
    return NSUserDefaults.standardUserDefaults().objectForKey(AppUserDefaults.SelectedMonth.value()) as AnyObject! as NSString!
}
set {
    NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: AppUserDefaults.SelectedMonth.value())
    NSUserDefaults.standardUserDefaults().synchronize()
}
}
Run Code Online (Sandbox Code Playgroud)