符合 Codable 协议的类因 encodeWithCoder 失败:无法识别的选择器发送到实例
Posted
技术标签:
【中文标题】符合 Codable 协议的类因 encodeWithCoder 失败:无法识别的选择器发送到实例【英文标题】:Class conforming to Codable protocol fails with encodeWithCoder: unrecognized selector sent to instance 【发布时间】:2018-05-19 10:30:54 【问题描述】:我知道有几个与此类似的问题,往往都围绕着没有正确遵守协议的类,但这不应该是这里的直接问题。
以下是当前给我这个问题的代码的精简版本:
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 函数中删除了所有属性
【问题讨论】:
【参考方案1】:你有很多不匹配的尝试和各种编码/解码机制。
NSKeyedArchiver
和NSKeyedUnarchiver
要求所有涉及的类型都符合NSCoding
协议。这是来自 Objective-C 框架的旧机制。
Codable
、Encoder
和 Decoder
协议是 Swift 4 的新协议。此类数据类型应与 Swift 编码器和解码器一起使用,例如 JSONEncoder
和 JSONDecoder
或 PropertyListEncoder
和 @987654330 @。
我建议您删除对NSCoder
的引用并删除对NSKeyedArchiver
和NSKeyedUnarchiver
的使用。由于您已经实现了Codable
协议,请使用适当的 Swift 编码器和解码器。在您的情况下,您想使用 PropertyListEncoder
和 PropertyListDecoder
。
完成后,您可能应该将 MyClass
更改为 struct
而不是 class
。
您还应该避免使用UserDefaults
来存储数据。将编码数据写入 plist 文件。
【讨论】:
谢谢,我已经很多年没有使用这些了,并且对新协议和旧方法感到困惑。我将更新我的答案以包含工作代码。 @CodeBender 你还没有发布答案。并且不要使用工作代码更新您的问题。如果需要,请发布实际答案。将问题和答案分开。【参考方案2】:这是从上面 rmaddy 提供的答案中得出的工作代码。
几个亮点:
-
将 MyClass 转换为 MyStruct
从我希望保存的对象中删除了 NSCoder 继承
删除了对
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)
【讨论】:
【参考方案3】:@CodeBender 的解决方案工作得很好,虽然 没有必要使用 init(from decoder: Decoder)
和 encode(to encoder: Encoder)
方法进行手动编码/解码,这样做只是违背了伟大的目的 可编码协议,除非您需要进行一些复杂级别的编码/解码。
下面是使用 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)
【讨论】:
以上是关于符合 Codable 协议的类因 encodeWithCoder 失败:无法识别的选择器发送到实例的主要内容,如果未能解决你的问题,请参考以下文章