使用NSCoding将自定义Swift类保存到UserDefaults

Geo*_*org 76 nsuserdefaults nscoding sigabrt swift

我目前正在尝试将自定义Swift类保存到NSUserDefaults.这是我的Playground的代码:

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")
Run Code Online (Sandbox Code Playgroud)

当我运行代码时,我收到以下错误

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

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

在带有消息的应用程序中,相同的代码也会崩溃

"属性列表格式无效:200(属性列表不能包含'CFType'类型的对象)"

有人可以帮忙吗?我在Maverick上使用Xcode 6.0.1.谢谢.

Ghu*_*ool 50

在Swift 4中,使用Codable.

在您的情况下,请使用以下代码.

class Blog : Codable {

   var blogName: String?

}
Run Code Online (Sandbox Code Playgroud)

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

var blog = Blog()
blog.blogName = "My Blog"
Run Code Online (Sandbox Code Playgroud)

现在编码如下:

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

并解码它像这样:

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


Dav*_*rry 45

第一个问题是你必须确保你有一个非破坏的类名:

@objc(Blog)
class Blog : NSObject, NSCoding {
Run Code Online (Sandbox Code Playgroud)

然后,您必须先将对象编码(转换为NSData),然后才能将其存储到用户默认值中:

ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")
Run Code Online (Sandbox Code Playgroud)

同样,要恢复对象,您需要取消归档它:

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

请注意,如果您没有在操场上使用它,那么您可以手动注册课程,这样会更简单:

if let data = ud.objectForKey("blog") as? NSData {
    let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data)
}
Run Code Online (Sandbox Code Playgroud)


Geo*_*org 21

正如@ dan-beaulieu建议我回答我自己的问题:

这是现在的工作代码:

注意:代码在Playgrounds中工作时不需要解析类名.

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
}
Run Code Online (Sandbox Code Playgroud)


Ron*_*ers 9

使用Swift 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

测试代码可能如下所示:

    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)")
Run Code Online (Sandbox Code Playgroud)

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


Jer*_*ker 7

在Swift 4中,您有一个替换NSCoding协议的新协议.它被调用Codable,它支持类和Swift类型!(枚举,结构):

struct CustomStruct: Codable {
    let name: String
    let isActive: Bool
}
Run Code Online (Sandbox Code Playgroud)


Vad*_*vin 6

这是Swift 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
    }
}
Run Code Online (Sandbox Code Playgroud)

假设我们要保存和加载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")

Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

51787 次

最近记录:

5 年,10 月 前