我知道有几个与此类似的问题,这些问题往往都围绕着不正确地遵守协议的类,但这不应该是这里的直接问题。
以下是目前给我这个问题的代码的精简版本:
enum Binary: Int {
case a = 0
case b = 1
case c = 9
}
final class MyClass: NSCoder {
var string: String?
var date: Date?
var binary: Binary = .c
override init() { }
enum CodingKeys: CodingKey {
case string, date, binary
}
}
extension MyClass: Codable {
convenience init(from decoder: Decoder) throws {
self.init()
let values = try decoder.container(keyedBy: CodingKeys.self)
string = try values.decode(String.self, forKey: .string)
date = try values.decode(Date.self, forKey: .date)
binary = try Binary(rawValue: values.decode(Int.self, forKey: .binary)) ?? .c
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(string, forKey: .string)
try container.encode(date, forKey: .date)
try container.encode(binary.rawValue, forKey: .binary)
}
}
我创建了以下类,然后尝试调用MyClass
以编写&读到UserDefaults
:
class MyClassController {
private let myClass: MyClass
init() {
self.myClass = MyClass()
self.myClass.string = "string"
self.myClass.date = Date()
self.myClass.binary = .a
}
func writeMyClass() {
let encodedData = NSKeyedArchiver.archivedData(withRootObject: myClass)
UserDefaults.standard.set(encodedData, forKey: String(describing: MyClass.self))
}
func readMyClass() {
if let decoded = UserDefaults.standard.object(forKey: String(describing: MyClass.self)) as? Data,
let myClass = NSKeyedUnarchiver.unarchiveObject(with: decoded as Data) as? MyClass {
print("string: \(myClass.string ?? "nil") date: \(myClass.date ?? Date()) binary: \(myClass.binary)")
}
}
}
一旦我调用writeMyClass函数,我就会收到此错误:
[DemoDecoder.MyClass encodeWithCoder:]:发送到的无法识别的选择器 实例#blahblah#
我还尝试了两件事:
func encode(with aCoder: NSCoder)
添加到MyClass
MyClass
&移除了所有属性CodingKeys
和init / encode函数答案 0 :(得分:4)
你有很多不匹配的尝试和各种编码/解码机制。
NSKeyedArchiver
和NSKeyedUnarchiver
要求所有相关类型都符合NSCoding
协议。这是Objective-C框架中较旧的机制。
协议Codable
,Encoder
和Decoder
是Swift 4的新功能。此类数据类型应与Swift编码器和解码器一起使用,例如JSONEncoder
和{{ 1}}或JSONDecoder
和PropertyListEncoder
。
我建议您删除对PropertyListDecoder
的引用,并删除NSCoder
和NSKeyedArchiver
的使用。由于您已实现NSKeyedUnarchiver
协议,请使用适当的Swift编码器和解码器。在您的情况下,您希望使用Codable
和PropertyListEncoder
。
完成后,您应该将PropertyListDecoder
更改为MyClass
而不是struct
。
您还应该避免使用class
来存储数据。将编码数据写入plist文件。
答案 1 :(得分:2)
这是从上面的rmaddy提供的答案中得出的工作代码。
一些亮点:
NSKeyedArchiver
& NSKeyedUnarchiver
UserDefaults
JSONEncoder
& JSONDecoder
写出struct Data
对象这是更新后的结构&我想保存的枚举:
enum Binary: Int {
case a = 0
case b = 1
case c = 9
}
struct MyStruct {
var string: String?
var date: Date?
var binary: Binary = .c
init() { }
enum CodingKeys: CodingKey {
case string, date, binary
}
}
extension MyStruct: Codable {
init(from decoder: Decoder) throws {
self.init()
let values = try decoder.container(keyedBy: CodingKeys.self)
string = try values.decode(String.self, forKey: .string)
date = try values.decode(Date.self, forKey: .date)
binary = try Binary(rawValue: values.decode(Int.self, forKey: .binary)) ?? .c
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(string, forKey: .string)
try container.encode(date, forKey: .date)
try container.encode(binary.rawValue, forKey: .binary)
}
}
处理阅读和更新的更新控制器类写输出。在我的情况下,写出JSON很好,所以我采用了这种方法。
class MyStructController {
private var myStruct: MyStruct
init() {
self.myStruct = MyStruct()
self.myStruct.string = "string"
self.myStruct.date = Date()
self.myStruct.binary = .a
}
func writeMyStruct() {
let encoder = JSONEncoder()
do {
let data = try encoder.encode(myStruct)
let documentDirectory = try FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor:nil,
create:false)
let url = documentDirectory.appendingPathComponent(String(describing: MyStruct.self))
try data.write(to: url)
} catch {
print(error.localizedDescription)
}
}
func readMyStruct() {
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor:nil,
create:false)
let url = documentDirectory.appendingPathComponent(String(describing: MyStruct.self))
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let myNewStruct = try decoder.decode(MyStruct.self, from: data)
print("string: \(myNewStruct.string ?? "nil") date: \(myNewStruct.date ?? Date()) binary: \(myNewStruct.binary)")
} catch {
print(error.localizedDescription)
}
}
}
答案 2 :(得分:0)
来自@CodeBender的解决方案效果很好,尽管无需使用init(from decoder: Decoder)
和encode(to encoder: Encoder)
方法进行手动编码/解码,但这只能达到目的除非您需要执行一些复杂级别的编码/解码,否则都应采用GREAT Codable 协议。
这是使用Codable协议的纯正好处的代码:
import UIKit
struct Movie: Codable {
enum MovieGenere: String, Codable {
case horror, drama, comedy, adventure, animation
}
var name : String
var moviesGenere : [MovieGenere]
var rating : Int
}
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
writeMyMovie(movie: Movie(name: "Titanic", moviesGenere: [Movie.MovieGenere.drama], rating: 1))
readMyMovie()
}
var documentDirectoryURL:URL? {
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor:nil,
create:false)
return documentDirectory.appendingPathComponent(String(describing: Movie.self))
} catch {
return nil
}
}
func writeMyMovie(movie:Movie) {
do {
let data = try JSONEncoder().encode(movie)
try data.write(to: documentDirectoryURL!) // CAN USE GUARD STATEMENT HERE TO CHECK VALID URL INSTEAD OF FORCE UNWRAPPING, IN MY CASE AM 100% SURE, HENCE NOT GUARDING ;)
} catch {
print(error.localizedDescription)
}
}
func readMyMovie() {
do {
let data = try Data(contentsOf: documentDirectoryURL!)
let movie = try JSONDecoder().decode(Movie.self, from: data)
print("MOVIE DECODED: \(movie.name)")
} catch {
print(error.localizedDescription)
}
}
}
快乐的编码/解码!