Swift 3从userDefaults保存并检索自定义对象

use*_*881 61 xcode nscoding ios swift swift3

我在Playground使用Swift 3,Xcode 8.0:

import Foundation
class Person: NSObject, NSCoding {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    required convenience init(coder aDecoder: NSCoder) {
        let name = aDecoder.decodeObject(forKey: "name") as! String
        let age = aDecoder.decodeObject(forKey: "age") as! Int
        self.init(
            name: name,
            age: age
        )
    }
    func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: "name")
        aCoder.encode(age, forKey: "age")
    }
}
Run Code Online (Sandbox Code Playgroud)

创建Person数组

let newPerson = Person(name: "Joe", age: 10)
var people = [Person]()
people.append(newPerson)
Run Code Online (Sandbox Code Playgroud)

编码数组

let encodedData = NSKeyedArchiver.archivedData(withRootObject: people)
print("encodedData: \(encodedData))")
Run Code Online (Sandbox Code Playgroud)

保存到userDefaults

let userDefaults: UserDefaults = UserDefaults.standard()
userDefaults.set(encodedData, forKey: "people")
userDefaults.synchronize()
Run Code Online (Sandbox Code Playgroud)

校验

print("saved object: \(userDefaults.object(forKey: "people"))")
Run Code Online (Sandbox Code Playgroud)

从userDefaults撤回

if let data = userDefaults.object(forKey: "people") {
    let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: data as! Data)
    print("myPeopleList: \(myPeopleList)")
}else{
    print("There is an issue")
}
Run Code Online (Sandbox Code Playgroud)

只需检查存档数据

if let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: encodedData){
   print("myPeopleList: \(myPeopleList)")
}else{
   print("There is an issue")
}
Run Code Online (Sandbox Code Playgroud)

我无法正确地将数据对象保存到userDefaults,此外,底部的检查会产生错误"致命错误:在展开Optional值时意外发现nil"."检查"行还显示保存的对象为零.这是我的对象的NSCoder中的错误吗?

Leo*_*bus 90

Swift 4注意

您可以再次在Playground中保存/测试您的值


斯威夫特3

UserDefaults需要在真实项目中进行测试.注意:无需强制同步.如果要在操场中测试编码/解码,可以使用键控归档器将数据保存到文档目录中的plist文件中.您还需要修复课程中的一些问题:

class Person: NSObject, NSCoding {
    let name: String
    let age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    required init(coder decoder: NSCoder) {
        self.name = decoder.decodeObject(forKey: "name") as? String ?? ""
        self.age = decoder.decodeInteger(forKey: "age")
    }

    func encode(with coder: NSCoder) {
        coder.encode(name, forKey: "name")
        coder.encode(age, forKey: "age")
    }
}
Run Code Online (Sandbox Code Playgroud)

测试:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // setting a value for a key
        let newPerson = Person(name: "Joe", age: 10)
        var people = [Person]()
        people.append(newPerson)
        let encodedData = NSKeyedArchiver.archivedData(withRootObject: people)
        UserDefaults.standard.set(encodedData, forKey: "people")

        // retrieving a value for a key
        if let data = UserDefaults.standard.data(forKey: "people"),
            let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Person] {
            myPeopleList.forEach({print( $0.name, $0.age)})  // Joe 10
        } else {
            print("There is an issue")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


ibu*_*ane 49

let age = aDecoder.decodeObject(forKey: "age") as! Int
Run Code Online (Sandbox Code Playgroud)

这已经改为Swift 3; 这不再适用于价值类型.现在正确的语法是:

let age = aDecoder.decodeInteger(forKey: "age")
Run Code Online (Sandbox Code Playgroud)

有各种不同类型的相关解码...()函数:

let myBool = aDecoder.decodeBoolean(forKey: "myStoredBool")
let myFloat = aDecoder.decodeFloat(forKey: "myStoredFloat")
Run Code Online (Sandbox Code Playgroud)

编辑:Swift 3中所有可能的decodeXXX函数的完整列表

编辑:

另一个重要注意事项:如果您之前保存的数据是使用较旧版本的Swift编码的,则必须使用decodeObject()对这些值进行解码,但是一旦使用encode(...)对数据进行重新编码,它就不再是如果它是值类型,则用decodeObject()解码.因此,Markus Wyss的答案将允许您处理使用Swift版本编码数据的情况:

self.age = aDecoder.decodeObject(forKey: "age") as? Int ?? aDecoder.decodeInteger(forKey: "age")
Run Code Online (Sandbox Code Playgroud)

  • 可笑的是没有Xcode警告旧代码会在Swift 3中破坏.感谢发布此内容! (18认同)
  • 如果它是一个bool,那么你需要结合使用`decodeBoolean()`函数?运算符,就像Int示例一样.`self.age = aDecoder.decodeObject(forKey:"myBoolean")as?布尔?aDecoder.decodeBoolean(forKey:"myBoolean")`不确定为什么不包括String; 任何没有专用函数的东西似乎仍然适用于`decodeObject()` (2认同)
  • 如果密钥不存在,如何管理swift3的解码?使用Swift 2我有:如果让ageTmp = decoder.decodeObject(forKey:"age")为?Int {self.age = ageTemp} else {self.age = 0} (2认同)
  • 在尝试解码之前,您应该使用if(aDecoder.containsValue(forKey:"age")){...}来查看该值是否存在. (2认同)

msw*_*yss 11

试试这个:

self.age = aDecoder.decodeObject(forKey: "age") as? Int ?? aDecoder.decodeInteger(forKey: "age")
Run Code Online (Sandbox Code Playgroud)


Bis*_*ung 6

在Swift 4中:

您可以使用Codable从Userdefaults中保存和检索自定义对象。如果您经常这样做,则可以添加为扩展名并按如下所示使用它。

extension UserDefaults {

   func save<T:Encodable>(customObject object: T, inKey key: String) {
       let encoder = JSONEncoder()
       if let encoded = try? encoder.encode(object) {
           self.set(encoded, forKey: key)
       }
   }

   func retrieve<T:Decodable>(object type:T.Type, fromKey key: String) -> T? {
       if let data = self.data(forKey: key) {
           let decoder = JSONDecoder()
           if let object = try? decoder.decode(type, from: data) {
               return object
           }else {
               print("Couldnt decode object")
               return nil
           }
       }else {
           print("Couldnt find key")
           return nil
       }
   }

}
Run Code Online (Sandbox Code Playgroud)

您的班级必须遵循Codable。它只是可编码和可解码协议的一种类型。

class UpdateProfile: Codable {
  //Your stuffs
}
Run Code Online (Sandbox Code Playgroud)

用法:

let updateProfile = UpdateProfile()

//To save the object
UserDefaults.standard.save(customObject: updateProfile, inKey: "YourKey")

//To retrieve the saved object
let obj = UserDefaults.standard.retrieve(object: UpdateProfile.self, fromKey: "YourKey")
Run Code Online (Sandbox Code Playgroud)

有关更多编码和解码自定义类型,请阅读Apple文档