2017-04-20 58 views
1

我試圖讓我的類符合NSCoding,但遇到了問題,因爲它的一個屬性是一個枚舉,像這樣:我如何編碼(與NSCoding)沒有rawValue的枚舉?

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?」的回答建議編碼enum的rawValue,然後在另一端從rawValue初始化它。但在這種情況下,Direction沒有rawValue

它確實有hashValue,這似乎很有前途。我可以毫無問題地編碼它的hashValue,並在另一端解碼回Int。但似乎沒有辦法從它的hashValue初始化枚舉,所以我不能把它變回Direction

如何編碼和解碼無價值的枚舉?

+0

所以你不能改變枚舉添加原始值。但是你可以對Enum做其他更改嗎? – Sweeper

+0

@Sweeper我希望能夠在不改變枚舉的情況下做到這一點。我很好奇,但是有什麼變化(除了添加一個rawValue)會有幫助嗎? – Robert

+0

我正在考慮添加一個初始化器 – Sweeper

回答

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:)來枚舉轉換爲字符串。要將字符串轉換回枚舉,只需訪問字典。

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是圖書館的一部分。併爲新方向添加按鍵。

+0

所以如果我明白這個權利,你將分配一個已知的值給枚舉和編碼的每個案例,然後從解碼值手動創建一個新的枚舉? – Robert

+1

@羅伯特吧!由於'switch'聲明必須是詳盡無遺的,所以如果出現一個新的'Direction'情況,編譯時會出現問題。 – Astoria

3

Swift 4中的新增枚舉枚舉可編碼。但是,它必須具有原始價值。您可以輕鬆地做,沒有額外的代碼,但是:

enum Direction : String, Codable { 
    case north 
    case south 
} 

,從而與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") 
    } 
} 
0

與SWIFT 4,如果您枚舉Direction可以有原始值,你可以使用CodableDecodableEncodable協議)來替代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"} 
*/ 
import Foundation 

let jsonString = """ 
{ 
    "direction" : "south" 
} 
""" 

let jsonData = jsonString.data(using: .utf8)! 
let decoder = JSONDecoder() 
let myClassInstance = try! decoder.decode(MyClass.self, from: jsonData) 
dump(myClassInstance) 

/* 
prints: 
▿ __lldb_expr_319.MyClass #0 
    - direction: __lldb_expr_319.Direction.south 
*/