试图通过AppDelegate加载保存在NSUserDefaults中的选项,总是返回nil

时间:2017-09-15 06:42:14

标签: ios swift swift3

我有一个Settings类,它有一堆UITextField属性,每个属性都要转换为Decimal变量。该部分全部有效,我使用NSUserDefaults保存UITextField内容和变量Decimals。 “设置”视图控制器正确加载并显示我之前保存的UITextField内容。

问题是我需要在没有设置视图控制器加载的情况下访问可选变量,当我尝试通过didFinishLaunchingWithOptions中的AppDelegate方法加载它们时,它总是返回nil。如果我在NSUserDefaults中加载变量(来自viewDidLoad()),或者在实际的“设置”视图控制器上加载变量,则值会正确显示。

我对Swift来说还是一个新手,但是我在这里忽略了什么想法?

编辑:我为延迟道歉;刚回到城里,我附上了解决我问题的代码。我还有一些我想要分配的变量,但是一旦我弄明白了,剩下的就很容易了。

截至目前,我甚至无法从视图控制器加载变量值。我知道当我编辑文本字段时,相应的Decimal?变量随之变化,它不会被保存或加载。我保存到UITextField的{​​{1}}是正确的节省/加载,而不是NSUserDefaults来自Decimal?值的UITextField变量。

class SettingsViewController: UIViewController {

var concrete360v: Decimal?

@IBOutlet var concrete360: UITextField?

//save the text field values to NSUserDefaults
func saveTextField(textField: String?, key: String) {
    let defaults = UserDefaults.standard
    defaults.set(textField, forKey: key)
}

//load text field value saved to NSUserDefault
func loadDefault(textField: UITextField?, fieldName: String) {
    if let text = UserDefaults.standard.object(forKey: fieldName) as? String 
{
        textField?.text = text
    }
}

//assign the text field values to their respective value variables
func assignTextToValue(textField: UITextField?, valueField: inout Decimal?) 
{
    if let text = textField?.text {
        valueField = Decimal(string: text)
        print("\(String(describing: concrete360v))")
    }
}

//save the value variable to NSUserDefaults
func saveValue(value: Decimal?, key: String) {
    let defaults = UserDefaults.standard
    defaults.set(value, forKey: key)
}

//load value variable from NSUserDefaults
func loadValue(value: inout Decimal?, valueName: String) {
    if let loadedValue = UserDefaults.standard.object(forKey: valueName) as? Decimal {  
        value = loadedValue
    }
}

//save ALL value variables to NSUserDefaults
func saveAllValues() {
    saveValue(value: concrete360v, key: "concrete360v")
    print("Saved all your variables")
}

//load ALL value variables from NSUserDefaults
func loadAllValues() {
    loadValue(value: &concrete360v, valueName: "concrete360v")
    print("\(String(describing: concrete360v))")
}

override func viewDidAppear(_ animated: Bool) {
    loadDefault(textField: concrete360, fieldName: "concrete360")
}

override func viewWillAppear(_ animated: Bool) {
    loadValue(value: &concrete360v, valueName: "concrete360v")
    print("\(String(describing: concrete360v))")
}

1 个答案:

答案 0 :(得分:1)

macOS和iOS上的CFPreferences / UserDefaults系统只能存储几种类型的对象。由于UserDefaults类的实际实现是在Objective-C(其名称为NSUserDefaults)中,因此这些类型都是Objective-C类型。在默认系统中读取和写入数值所涉及的类型称为NSNumber,它基本上只是围绕C&C的原始整数和浮点类型的面向对象的包装器。大多数Swift整数和浮动类型都可以通过Swift< - > Objective-C桥接魔法强制转换为NSNumber,这就是为什么你可以发送类似IntDouble的内容到UserDefaults的方法,并期望它的工作。但是,Decimal稍有不同,因为它与Objective-C桥接为NSDecimalNumber,它是NSNumber子类。由于它符合NSNumber的API,UserDefaults能够将值写入默认系统;但是,默认系统的底层存储不保留原始类,而只是写入数值。因此,如果您将1.23写为Decimal,则会写入数字1.23,但当您将其读回时,UserDefaults会看到1.23存储,并将其导入为花园种类NSNumber,可能包含Double

如果你进行一点测试,你可以看到这个:

import Foundation

let foo = 2.3 as Decimal

UserDefaults.standard.set(foo, forKey: "Foo")

print(type(of: UserDefaults.standard.value(forKey: "Foo")!))

输出:

__NSCFNumber

NSNumber的私有子类(存在是为了促进NSNumberCFNumberRef之间的免费桥接,但这超出了此答案的范围)

无论如何,NSNumber神奇地连接到大多数标准Swift整数和浮点类型,但是系统没有魔法将它桥接到Decimal。您可以将NSDecimalNumber投射到Decimal,但可以使用普通的NSNumber进行尝试,然后获得nil。欢迎来到Swift< - > Objective-C桥的野性,毛茸茸的世界。

无论如何,您仍然可以通过转换而不是转换来将其转换为Decimal

import Foundation

let foo = 2.3 as Decimal

UserDefaults.standard.set(foo, forKey: "Foo")

if let bar = UserDefaults.standard.value(forKey: "Foo") as? Double {
    let baz = Decimal(bar)
    print("got baz: \(baz)")
} else {
    print("bar is nil")
}

输出:

got baz: 2.3