我试图让我的班级符合NSCoding
,但遇到问题,因为其中一个属性是枚举,如下所示:
enum Direction {
case north
case south
}
如果枚举是可编码的,我可以这样做:
class MyClass: NSObject, NSCoding {
var direction: Direction!
required init(coder aDecoder: NSCoder) {
direction = aDecoder.decodeObject(forKey: "direction") as! Direction
}
func encode(with aCoder: NSCoder) {
aCoder.encode(direction, forKey: "direction")
}
}
但枚举不可编码,因此encode()
会引发错误。
" How do I encode enum using NSCoder in swift?"的答案建议对枚举rawValue
进行编码,然后在另一端从rawValue
初始化它。但在这种情况下,Direction
没有rawValue
!
确实有一个hashValue
,看起来很有希望。我可以毫无问题地对其hashValue
进行编码,并在另一端解码回Int
。但似乎没有一种方法可以从hashValue
初始化枚举,因此我无法将其转换为Direction
。
如何编码和解码无值枚举?
答案 0 :(得分:5)
Swift 4中的新功能,枚举 可编码。但是,它必须具有原始值。但是,您可以轻松完成任何其他代码:
enum Direction : String, Codable {
case north
case south
}
要使您的Codable枚举与NSCoding类MyClass一起使用,请像这样实现init(coder:)
和encode(with:)
:
class MyClass: NSObject, NSCoding {
var direction: Direction!
required init(coder aDecoder: NSCoder) {
direction = (aDecoder as! NSKeyedUnarchiver).decodeDecodable(Direction.self, forKey: "direction")
}
func encode(with aCoder: NSCoder) {
try? (aCoder as! NSKeyedArchiver).encodeEncodable(direction, forKey: "direction")
}
}
答案 1 :(得分:1)
您可以定义一些键并存储它们。
fileprivate let kDirectionKeyNorth = 1
fileprivate let kDirectionKeySouth = 2
// ...
required init(coder aDecoder: NSCoder) {
let key = aDecoder.decodeInteger(forKey: "direction")
switch key {
case kDirectionKeyNorth:
// ...
}
}
// and vise versa
这有点棘手。你应该总是照顾Direction
所属的图书馆。并为新方向添加键。
答案 2 :(得分:1)
我认为在这里为枚举添加原始值是代码最少的解决方案,并且是最易维护的。因此,如果您可以修改枚举,请添加原始值。
现在让我们假设您无法修改枚举。你仍然可以通过几种方式做到这一点。
第一个,我认为非常难看,是添加一个extension
枚举并添加一个这样的静态方法:
static func direction(from rawValue: String) -> Direction {
switch rawValue {
case: "north": return .north
case: "south": return .south
default: fatalError()
}
}
要将Direction
转换为可编码值,请使用String(describing:)
将枚举转换为字符串。要将字符串转换回枚举,只需使用上面的方法。
第二个,稍好一点,但仍然不如添加原始值那么好。
你使用字典:
let enumValueDict: [String: Direction] = [
"north": .north, "south": .south
]
要将Direction
转换为可编码值,请使用String(describing:)
将枚举转换为字符串。要将字符串转换回枚举,只需访问字典。
答案 3 :(得分:0)
使用Swift 4,如果您的枚举Direction
可以包含原始值,则可以使用Codable
(Decodable
和Encodable
协议)作为{{1}的替代方案}:
NSCoding
用法:
enum Direction: String, Codable {
case north, south
}
final class MyClass: Codable {
let direction: Direction
init(direction: Direction) {
self.direction = direction
}
}
import Foundation
let encoder = JSONEncoder()
let myClass = MyClass(direction: .north)
if let data = try? encoder.encode(myClass),
let jsonString = String(data: data, encoding: .utf8) {
print(jsonString)
}
/*
prints:
{"direction":"north"}
*/
答案 4 :(得分:0)
一种选择是使枚举符合Codable
。可以通过任何所需的机制(例如,使用Int或String等原始值,或实现方法)。
因此,假设我们具有以下条件:
enum Direction { case north, south, east, west }
extension Direction: Codable {
// etc etc - make it conform
}
final class MyClass: NSObject {
var direction: Direction!
}
然后在NSCoding
的必需方法中,执行以下操作:
extension MyClass: NSCoding {
func encode(with aCoder: NSCoder) {
guard let keyedCoder = aCoder as? NSKeyedArchiver else {
fatalError("Must use Keyed Coding")
}
try! keyedCoder.encodeEncodable(direction, forKey: "direction")
}
convenience init?(coder aDecoder: NSCoder) {
self.init()
guard let keyedDecoder = aDecoder as? NSKeyedUnarchiver else {
fatalError("Must use Keyed Coding")
}
direction = keyedDecoder.decodeDecodable(Direction.self, forKey: "direction") ?? .north
}
}
因此,这是可行的,因为NSKeyedArchiver
和NSKeyedUnarchiver
知道Codable
,而NSCoder
却不知道。但是,由于现已弃用NSArchiver
和NSUnarchiver
,因此从根本上不建议使用非键归档,因此对于您的项目,使用{{1 }},以这种方式-假设您测试了代码。