如何编码(使用NSCoding)没有rawValue的枚举?

时间:2017-04-20 06:46:57

标签: swift encoding enums swift3 nscoding

我试图让我的班级符合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

如何编码和解码无值枚举?

5 个答案:

答案 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可以包含原始值,则可以使用CodableDecodableEncodable协议)作为{{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
    }
}

因此,这是可行的,因为NSKeyedArchiverNSKeyedUnarchiver知道Codable,而NSCoder却不知道。但是,由于现已弃用NSArchiverNSUnarchiver,因此从根本上不建议使用非键归档,因此对于您的项目,使用{{1 }},以这种方式-假设您测试了代码。