2014-10-20 103 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