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

时间:2014-06-04 08:47:09

标签: 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()
}
}

该方法可以正常获取/设置属性,但是当第一次执行应用程序并且用户默认值为空时,我在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

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

func objectForKey(defaultName: String!) -> AnyObject!

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

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

2 个答案:

答案 0 :(得分:12)

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

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

!在一个声明中意味着该变量是隐式展开的(在Swift书上,读取&#34;隐式展开的Optionals&#34;和#34;无主参考和隐式展开的可选属性&#34;章节以更好地理解它的含义< / p>

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

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

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

没有给出错误。(结果没有返回但是objectForKey调用没有失败) 因此,这让我再次走上正确的路,并理解隐含解开的选项的含义

解决方案很简单,因为返回的值可以是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()
}
}

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

这就像写作

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

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

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

答案 1 :(得分:4)

试试这个

if let myObject : AnyObject = defaults.objectForKey(defaultKey){
    // grab myObject here
    return myObject;
}else{
    // .. //
    return nil;
}