我有一个班级
class Player {
var name = ""
func encodeWithCoder(encoder: NSCoder) {
encoder.encodeObject(name)
}
func initWithCoder(decoder: NSCoder) -> Player {
self.name = decoder.decodeObjectForKey("name") as String
return self
}
init(coder aDecoder: NSCoder!) {
self.name = aDecoder.decodeObjectForKey("name") as String
}
init(name: String) {
self.name = name
}
}
我希望将其序列化并保存到用户默认值。
首先,我不确定如何正确编写编码器和解码器。所以对于init我写了两个方法。
当我尝试执行此代码时:
func saveUserData() {
let player1 = Player(name: "player1")
let myEncodedObject = NSKeyedArchiver.archivedDataWithRootObject(player1)
NSUserDefaults.standardUserDefaults().setObject(myEncodedObject, forKey: "player")
}
app崩溃,我收到此消息:
*** NSForwarding: warning: object 0xebf0000 of class '_TtC6GameOn6Player' does not implement methodSignatureForSelector: -- trouble ahead
我做错了什么?
答案 0 :(得分:11)
NSKeyedArchiver
仅适用于Objective-C类,而不适用于纯Swift类。您可以通过使用@objc
属性标记类或通过继承Objective-C类(例如NSObject
)来将类桥接到Objective-C。
有关详细信息,请参阅Using Swift with Cocoa and Objective-C。
答案 1 :(得分:11)
使用XCode 7.1.1测试, Swift 2.1 & iOS 9
您可以选择保存自定义对象(数组):
我将Core数据排除在讨论之外,但是想要告诉你为什么你应该更好地使用NSKeyedArchiver而不是NSUserdefaults。
我已更新您的Player类并为这两个选项提供了方法。虽然两种选择都有效,但如果比较'load& amp;保存'方法你会看到 NSKeydArchiver需要更少的代码来处理自定义对象数组。使用NSKeyedArchiver,您可以轻松地将内容存储到单独的文件中,而不必担心每个属性的唯一“键”名称。
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// create some data
let player1 = Player(name: "John")
let player2 = Player(name: "Patrick")
let playersArray = [player1, player2]
print("--- NSUserDefaults demo ---")
Player.savePlayersToUserDefaults(playersArray)
if let retreivedPlayers = Player.loadPlayersFromUserDefaults() {
print("loaded \(retreivedPlayers.count) players from NSUserDefaults")
print("\(retreivedPlayers[0].name)")
print("\(retreivedPlayers[1].name)")
} else {
print("failed")
}
print("--- file demo ---")
Player.savePlayersToDisk(playersArray)
if let retreivedPlayers = Player.loadPlayersFromDisk() {
print("loaded \(retreivedPlayers.count) players from disk")
print("\(retreivedPlayers[0].name)")
print("\(retreivedPlayers[1].name)")
} else {
print("failed")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
我已按如下方式测试此类(单视图应用程序,在ViewController的viewDidLoad方法中)
true
如上所述,两种方法都产生相同的结果
同样在现实生活中,你可以做更好的错误处理,作为存档& unarchiving可能会失败。
答案 2 :(得分:9)
在Swift 4中,你不再需要NSCoding了!有一个名为Codable
的新协议!
class Player: Codable {
var name = ""
init(name: String) {
self.name = name
}
}
Codable
还支持Enums和Structs,因此您可以根据需要将播放器类重写为结构体!
struct Player: Codable {
let name: String
}
要在Userdefaults中保存播放器:
let player = Player(name: "PlayerOne")
try? UserDefaults.standard.set(PropertyListEncoder().encode(player), forKey: "player")
注意:PropertyListEncoder()是框架Foundation
中的一个类要检索:
let encoded = UserDefault.standard.object(forKey: "player") as! Data
let storedPlayer = try! PropertyListDecoder().decode(Player.self, from: encoded)
有关详情,请参阅https://developer.apple.com/documentation/swift/codable
答案 3 :(得分:4)
我有一个班级
class Player { var name = "" init(name: String) { self.name = name } }
我希望将其序列化并保存为用户默认值。
在Swift 4 / iOS 11中,有一种全新的方法可以做到这一点。它的优点是任何 Swift对象都可以使用它 - 不仅仅是类,还有结构和枚举。
您会注意到我已经省略了与NSCoding相关的方法,因为您不需要它们用于此目的。如你所知,你可以在这里采用NSCoding;但你不必。 (并且结构或枚举根本不能采用NSCoding。)
首先声明您的课程采用Codable协议:
class Player : Codable {
var name = ""
init(name: String) {
self.name = name
}
}
然后将它序列化为可以存储在UserDefaults中的Data对象(NSData)变得很简单。最简单的方法是使用属性列表作为中介:
let player = Player(name:"matt")
try? UserDefaults.standard.set(PropertyListEncoder().encode(player),
forKey:"player")
如果您使用这种方法,现在让我们证明您可以从UserDefaults中取出同一个播放器:
if let data = UserDefaults.standard.object(forKey:"player") as? Data {
if let p = try? PropertyListDecoder().decode(Player.self, from: data) {
print(p.name) // "matt"
}
}
如果你宁愿通过NSKeyedArchiver / NSKeyedUnarchiver,你也可以这样做。实际上,在某些情况下您必须这样做:您将获得一个NSCoder,并且您需要在其中编码您的Codable对象。在最近的测试中,Xcode 9也引入了一种方法。例如,如果您正在编码,则将NSCoder转换为NSKeyedArchiver并调用encodeEncodable
。
答案 4 :(得分:4)
使用可编码新的ios 11协议,您现在可以让您的类使用 JSONEncoder 和 JSONDecoder <来实现它并归档/取消归档它的对象/ p>
struct Language: Codable {
var name: String
var version: Int
}
let swift = Language(name: "Swift", version: 4)
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(swift) {
// save `encoded` somewhere
}
if let encoded = try? encoder.encode(swift) {
if let json = String(data: encoded, encoding: .utf8) {
print(json)
}
let decoder = JSONDecoder()
if let decoded = try? decoder.decode(Language.self, from: encoded) {
print(decoded.name)
}