[英][__SwiftValue encodeWithCoder:]: unrecognized selector sent to instance
[英]Class conforming to Codable protocol fails with encodeWithCoder: unrecognized selector sent to instance
我知道有幾個與此類似的問題,往往都圍繞着不正確遵守協議的類,但這不應該是這里的直接問題。
以下是目前給我這個問題的代碼的精簡版本:
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 函數中刪除了所有屬性您有很多不匹配的嘗試和各種編碼/解碼機制。
NSKeyedArchiver
和NSKeyedUnarchiver
要求所有涉及的類型都符合NSCoding
協議。 這是來自 Objective-C 框架的舊機制。
Codable
、 Encoder
和Decoder
協議是 Swift 4 的新增內容。此類數據類型應與 Swift 編碼器和解碼器一起使用,例如JSONEncoder
和JSONDecoder
或PropertyListEncoder
和PropertyListDecoder
。
我建議您刪除對NSCoder
的引用並刪除NSKeyedArchiver
和NSKeyedUnarchiver
的使用。 由於您已經實現了Codable
協議,因此請使用合適的 Swift 編碼器和解碼器。 在您的情況下,您想使用PropertyListEncoder
和PropertyListDecoder
。
完成后,您可能應該將MyClass
更改為struct
而不是class
。
您還應該避免使用UserDefaults
來存儲數據。 而是將編碼數據寫入 plist 文件。
這是從上面 rmaddy 提供的答案派生的工作代碼。
幾個亮點:
NSKeyedArchiver
和NSKeyedUnarchiver
調用UserDefaults
JSONEncoder
& JSONDecoder
寫出 structData
對象寫入文件系統這是我希望保存的更新后的結構和枚舉:
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)
}
}
}
@CodeBender 的解決方案工作得很好,盡管不需要使用init(from decoder: Decoder)
fromdecoder 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)
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.