2014-10-20 98 views
57

我目前正在尝试将自定义Swift类保存到NSUserDefaults。下面是我的游乐场代码:将具有NSCoding的自定义SWIFT类保存到UserDefaults

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类型的对象)时无效”

任何人都可以帮忙吗?我在Maverick上使用Xcode 6.0.1。谢谢。

+2

你应该张贴您的工作代码作为一个答案,让自己的一些选票,因为这里唯一的答案是链路只能回答这被认为是无答案的堆栈溢出的指导方针。 – 2015-10-27 23:38:41

+0

这里有一个很好的教程:https://ios8programminginswift.wordpress.com/2014/08/17/persisting-data-with-nsuserdefaults/这里有一个很好的reddit讨论:http://www.reddit.com/r/ swift/comments/28sp3z/whats_the_best_way_to_achieve_persistence_in /另一个好帖子在这里:http://www.myswiftjourney.me/2014/10/01/simple-persistent-storage-using-nsuserdefaults/对于完整的对象存储,我建议完整的NSCoding - 两下面的例子,从我的第二个完整的类结构:http://stackoverflow.com/questions/26233067/simple-persistent-storage-in-swift/26233274#26233274 http:// stackoverfl – 2014-10-20 15:52:56

+0

工作代码发布如下。 – Georg 2017-03-16 18:41:35

回答

16

正如@丹蟠龙建议我回答我的问题:

这是现在的工作代码:

注意:对于代码在Playgrounds中工作,不需要对类名进行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 
} 
41

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

@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") 
} 

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

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

谢谢。那样做了。 NSKeyedUnarchiver是缺少的一块。我会用一个工作示例更新我的问题。看起来你可以省略unarc.setClass,而是将unarc.decodeObjectForKey向下转换为 - 在这种情况下 - Blog类。 – Georg 2014-10-20 17:32:57

+1

如果你在游乐场工作,你只需要'setClass',由于种种原因,课程不会自动在那里注册。 – 2014-10-20 18:20:41

+0

上面的'blog'变量不是'if let data = ud.objectForKey(“blog”)中的自定义对象吗? NSData {let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data) }',在我的情况下,我需要将它转换为像这样的自定义对象if if data = ud.objectForKey(“blog”)as? NSData {let} blog = NSKeyedUnarchiver.unarchiveObjectWithData(data) if let blog = blog as?博客{ 返回此博客//这是来自nsuserdefaults的自定义对象 } }' – CaffeineShots 2015-07-07 07:18:41

9

测试与雨燕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。你可以在我的回答here中找到更多关于他们的区别。

+0

只是供参考:blogName是一个可选的原因是该对象镜像用户具有的实际博客。一旦用户添加新博客的URL,博客名称就会自动从标题标签派生。所以在创建对象时没有博客名称,我想避免调用一个新的博客“未命名”或类似的东西。 – Georg 2016-01-20 10:40:00

1

我,我用我的结构是比较容易

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) 
      NSUserDefaults.standardUserDefaults().synchronize() 
     } 
    } 

} 



Use : let userinfo = UserDefaults.UserInformation 
-3

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

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

斯威夫特3版本:

class CustomClass: NSObject, NSCoding { 

var name = "" 
var isActive = false 

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

}

5

在斯威夫特4,你有一个新的协议,取代了NSCoding协议。它被称为Codable,它支持类和Swift类型! (枚举,结构):

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

类实现了Codable并且有一个可选的字符串数组:''试图插入非属性列表对象' – 2017-11-28 10:25:04

+0

这个答案是完全错误的。 'Codable'不**代替'NSCoding'。将对象直接保存到UserDefaults的唯一方法是使类NSCoding符合。由于'NSCoding'是一个'类协议',它不能应用于struct/enum类型。但是,您仍然可以使您的struct/enum'Coding'兼容,并使用'JSONSerializer'编码为'Data',它可以存储在'UserDefaults'中。 – Josh 2017-11-29 17:38:05