保存自定义斯威夫特类NSCoding到UserDefaults保存自定义斯威夫特类NSCoding到

2019-05-10 09:00发布

目前我正在试图将自定义斯威夫特类保存到NSUserDefaults的。 下面是我的游乐场的代码:

import Foundation

class Blog : NSObject, NSCoding {

    var blogName: String?

    override init() {}

    required init(coder aDecoder: NSCoder) {
        if let blogName = aDecoder.decodeObjectForKey("blogName") as? String {
            self.blogName = blogName
        }
    }

    func encodeWithCoder(aCoder: NSCoder) {
        if let blogName = self.blogName {
            aCoder.encodeObject(blogName, forKey: "blogName")
        }
    }

}

var blog = Blog()
blog.blogName = "My Blog"

let ud = NSUserDefaults.standardUserDefaults()    
ud.setObject(blog, forKey: "blog")

当我运行的代码,我得到以下错误

执行被中断,原因是:信号SIGABRT。

在最后一行( ud.setObject ...)

相同的代码与所述消息中的应用程序也崩溃时

“财产清单格式无效:200(属性列表不能包含的类型‘CFType’对象)”

任何人都可以帮忙吗? 我使用Xcode的特立独行6.0.1。 谢谢。

Answer 1:

第一个问题是你必须确保你有一个非错位类名:

@objc(Blog)
class Blog : NSObject, NSCoding {

然后,你必须编码对象(到一个NSData),然后才能将其存储到用户的默认值:

ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")

同样,恢复则需要解除封存对象:

if let data = ud.objectForKey("blog") as? NSData {
    let unarc = NSKeyedUnarchiver(forReadingWithData: data)
    unarc.setClass(Blog.self, forClassName: "Blog")
    let blog = unarc.decodeObjectForKey("root")
}

请注意,如果你不是在操场上使用它是因为你不必用手注册类稍微简单:

if let data = ud.objectForKey("blog") as? NSData {
    let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data)
}


Answer 2:

在斯威夫特4,使用可编码。

在你的情况下,使用下面的代码。

struct Blog : Codable {

   var blogName: String?

}

现在创建它的对象。 例如:

var blog = Blog()
blog.blogName = "My Blog"

现在,它编码是这样的:

if let encoded = try? JSONEncoder().encode(blog) {
    UserDefaults.standard.set(encoded, forKey: "blog")
}

像这样对其进行解码:

if let blogData = UserDefaults.standard.data(forKey: "blog"),
    let blog = try? JSONDecoder().decode(Blog.self, from: blogData) {
}


Answer 3:

作为@丹蟠龙建议我回答我的问题:

这里是工作的代码现在:

注:类名的Demangling是没有必要的代码在游乐场工作。

import Foundation

class Blog : NSObject, NSCoding {

    var blogName: String?

    override init() {}

    required init(coder aDecoder: NSCoder) {
        if let blogName = aDecoder.decodeObjectForKey("blogName") as? String {
            self.blogName = blogName
        }
    }

    func encodeWithCoder(aCoder: NSCoder) {
        if let blogName = self.blogName {
            aCoder.encodeObject(blogName, forKey: "blogName")
        }
    }

}

let ud = NSUserDefaults.standardUserDefaults()

var blog = Blog()
blog.blogName = "My Blog"

ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")

if let data = ud.objectForKey("blog") as? NSData {
    let unarc = NSKeyedUnarchiver(forReadingWithData: data)
    let newBlog = unarc.decodeObjectForKey("root") as Blog
}


Answer 4:

斯威夫特2.1和Xcode中7.1.1进行测试

如果你不需要BLOGNAME是一个可选的(我认为你不这样做),我会建议一个稍微不同的实现:

class Blog : NSObject, NSCoding {

    var blogName: String

    // designated initializer
    //
    // ensures you'll never create a Blog object without giving it a name
    // unless you would need that for some reason?
    //
    // also : I would not override the init method of NSObject

    init(blogName: String) {
        self.blogName = blogName

        super.init()        // call NSObject's init method
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(blogName, forKey: "blogName")
    }

    required convenience init?(coder aDecoder: NSCoder) {
        // decoding could fail, for example when no Blog was saved before calling decode
        guard let unarchivedBlogName = aDecoder.decodeObjectForKey("blogName") as? String
            else {
                // option 1 : return an default Blog
                self.init(blogName: "unnamed")
                return

                // option 2 : return nil, and handle the error at higher level
        }

        // convenience init must call the designated init
        self.init(blogName: unarchivedBlogName)
    }
}

测试代码看起来是这样的:

    let blog = Blog(blogName: "My Blog")

    // save
    let ud = NSUserDefaults.standardUserDefaults()
    ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")
    ud.synchronize()

    // restore
    guard let decodedNSData = ud.objectForKey("blog") as? NSData,
    let someBlog = NSKeyedUnarchiver.unarchiveObjectWithData(decodedNSData) as? Blog
        else {
            print("Failed")
            return
    }

    print("loaded blog with name : \(someBlog.blogName)")

最后,我想指出的是,这将是更容易使用的NSKeyedArchiver和您的自定义对象的数组保存到直接文件,而不是使用NSUserDefaults的。 你可以找到更多关于他们在我的答案的差异在这里 。



Answer 5:

在斯威夫特4,你有一个新的协议,取代了NSCoding协议。 这就是所谓的Codable ,并支持类和斯威夫特的类型! (枚举,结构):

struct CustomStruct: Codable {
    let name: String
    let isActive: Bool
}


Answer 6:

斯威夫特3版本:

class CustomClass: NSObject, NSCoding {

    let name: String
    let isActive: Bool

    init(name: String, isActive: Bool) {
        self.name = name
        self.isActive = isActive
    }

    // MARK: NSCoding

    required convenience init?(coder decoder: NSCoder) {
        guard let name = decoder.decodeObject(forKey: "name") as? String,
            let isActive = decoder.decodeObject(forKey: "isActive") as? Bool
            else { return nil }

        self.init(name: name, isActive: isActive)
    }

    func encode(with coder: NSCoder) {
        coder.encode(self.name, forKey: "name")
        coder.encode(self.isActive, forKey: "isActive")
    }
}


Answer 7:

下面是夫特4和5的完整解决方案。

首先,实现辅助方法UserDefaults扩展:

extension UserDefaults {

    func set<T: Encodable>(encodable: T, forKey key: String) {
        if let data = try? JSONEncoder().encode(encodable) {
            set(data, forKey: key)
        }
    }

    func value<T: Decodable>(_ type: T.Type, forKey key: String) -> T? {
        if let data = object(forKey: key) as? Data,
            let value = try? JSONDecoder().decode(type, from: data) {
            return value
        }
        return nil
    }
}

再说了,我们要保存和加载自定义对象Dummy用2个默认字段。 Dummy必须符合Codable

struct Dummy: Codable {
    let value1 = "V1"
    let value2 = "V2"
}

// Save
UserDefaults.standard.set(encodable: Dummy(), forKey: "K1")

// Load
let dummy = UserDefaults.standard.value(Dummy.self, forKey: "K1")



Answer 8:

我用我自己的结构。 这是很容易。

struct UserDefaults {
    private static let kUserInfo = "kUserInformation"

    var UserInformation: DataUserInformation? {
        get {
            guard let user = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaults.kUserInfo) as? DataUserInformation else {
                return nil
            }
            return user
        }
        set {

            NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: UserDefaults.kUserInfo)
        }
    }
}

使用方法:

let userinfo = UserDefaults.UserInformation


Answer 9:

我知道这个问题是旧的,但我写了一个小库 ,可能会有所帮助:

你存储的对象是这样的:

class MyObject: NSObject, Codable {

var name:String!
var lastName:String!

enum CodingKeys: String, CodingKey {
    case name
    case lastName = "last_name"
   }
}

self.myStoredPref = Pref<MyObject>(prefs:UserDefaults.standard,key:"MyObjectKey")
let myObject = MyObject()
//... set the object values
self.myStoredPref.set(myObject)

然后以提取对象返回到其原始值:

let myStoredValue: MyObject = self.myStoredPref.get()


Answer 10:

你不能存储直接在属性列表的对象; 你只能存储单个字符串或其他原始类型(整数等),所以你需要把它存储为单个字符串,如:

   override init() {
   }

   required public init(coder decoder: NSCoder) {
      func decode(obj:AnyObject) -> AnyObject? {
         return decoder.decodeObjectForKey(String(obj))
      }

      self.login = decode(login) as! String
      self.password = decode(password) as! String
      self.firstname = decode(firstname) as! String
      self.surname = decode(surname) as! String
      self.icon = decode(icon) as! UIImage
   }

   public func encodeWithCoder(coder: NSCoder) {
      func encode(obj:AnyObject) {
         coder.encodeObject(obj, forKey:String(obj))
      }

      encode(login)
      encode(password)
      encode(firstname)
      encode(surname)
      encode(icon)
   }


文章来源: Saving custom Swift class with NSCoding to UserDefaults