我在Obj-C中做了很多NSCoding归档,但我不确定它如何处理Swift中的结构,也不确定具有可选值的数组。这是我的代码:
public struct SquareCoords {
var x: Int, y: Int
}
这是我需要存储的类:
public class Player: NSCoding {
var playerNum: Int
var name = ""
private var moveHistory: [SquareCoords?] = []
init (playerNum: Int, name: String) {
self.playerNum = playerNum
self.name = name
}
public required init(coder aDecoder: NSCoder!) {
playerNum = aDecoder.decodeIntegerForKey("playerNumKey")
name = aDecoder.decodeObjectForKey("nameKey") as String
moveHistory = aDecoder.decodeObjectForKey("moveHistoryKey") as [SquareCoords?]
}
public func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeInteger(playerNum, forKey: "playerNumKey")
aCoder.encodeObject(name, forKey: "nameKey")
aCoder.encodeObject(moveHistory, forKey: "moveHistoryKey")
}
...
在编码器init的最后一行,我在XCode中收到以下错误消息:
'AnyObject' is not convertible to [SquareCoords?]'
并在encodeWithEncoder的最后一行:
Extra argument 'forKey' in call
有人能让我朝着正确的方向前进吗?
答案 0 :(得分:27)
在The Swift Programming Language中,Apple表示:
Swift提供了两种特殊类型别名,用于处理非特定类型:
-AnyObject
可以表示任何类类型的实例 -Any
可以表示任何类型的实例,包括函数类型。
知道,键入SquareCoords
(Swift结构)并键入[SquareCoords]
(Swift数组的Swift数组)不能符合协议AnyObject
。
另一方面,decodeObjectForKey:
需要一个符合协议AnyObject
的参数,encodeObject:forKey:
返回AnyObject
。因此,以下两行无法编译:
moveHistory = aDecoder.decodeObjectForKey("moveHistoryKey") as [SquareCoords?]
aCoder.encodeObject(moveHistory, forKey: "moveHistoryKey")
因此,除非您找到一种方法使SquareCoords
符合协议AnyObject
(我不知道是否可行),您必须将SquareCoords
从Swift Structure转换为类。
<强> PS:强>
此时,您可能会问:“好的,但是类型String
- 实际上是Swift Struct - 怎么可能符合协议AnyObject
?”好吧,那是因为String
无缝连接到基金会的NSString
班级Array
,Dictionary
以同样的方式桥接到NSArray
和NSDictionary
。如果您想更好地了解它,请阅读this blog post。
答案 1 :(得分:10)
我不确定究竟是什么问题,但如果您使用NSMutableArray而不是Swift数组,问题就解决了:
public struct SquareCoords {
var x: Int, y: Int
}
public class Player: NSCoding {
var playerNum: Int
var name = ""
var moveHistory: NSMutableArray = NSMutableArray()
init (playerNum: Int, name: String) {
self.playerNum = playerNum
self.name = name
}
public required init(coder aDecoder: NSCoder!) {
playerNum = aDecoder.decodeIntegerForKey("playerNumKey")
name = aDecoder.decodeObjectForKey("nameKey") as String
moveHistory = aDecoder.decodeObjectForKey("moveHistoryKey") as NSMutableArray
}
public func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeInteger(playerNum, forKey: "playerNumKey")
aCoder.encodeObject(name, forKey: "nameKey")
aCoder.encodeObject(moveHistory, forKey: "moveHistoryKey")
}
}
似乎是这样的情况,当aDecoder.decodeObjectForKey返回一个隐式解包的AnyObject时,它不会被转换为SquareCoords数组。
进一步玩这个,我注意到它可能与结构的使用有关。 (你正在创建一个值类型的结构数组。)这是一个猜测,但我注意到如果一个类类型用于SquareCoords没有问题,例如。
public class SquareCoords {
var x: Int = 0, y: Int = 0
}
public class Player: NSCoding {
var playerNum: Int
var name = ""
private var moveHistory: [SquareCoords] = [SquareCoords]()
init (playerNum: Int, name: String) {
self.playerNum = playerNum
self.name = name
}
public required init(coder aDecoder: NSCoder!) {
playerNum = aDecoder.decodeIntegerForKey("playerNumKey")
name = aDecoder.decodeObjectForKey("nameKey") as String
moveHistory = aDecoder.decodeObjectForKey("moveHistoryKey") as [SquareCoords]
}
public func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeInteger(playerNum, forKey: "playerNumKey")
aCoder.encodeObject(name, forKey: "nameKey")
aCoder.encodeObject(moveHistory, forKey: "moveHistoryKey")
}
}
由于某种原因,AnyObject的转换可能会失败到结构数组。 - 我确定其他人可以提供更多的见解,希望这有所帮助!斯威夫特可以狂暴:D
答案 2 :(得分:8)
此处给出的其他答案可以解决您的问题。但是,我最近在尝试归档我的Swift结构时遇到了类似的问题,并提出了一种有趣的方法来解决这个问题。 NSCoding不支持结构。
实质上,以下方法涉及将结构属性转换为字典元素。但是,它优雅地使用协议。您需要做的就是定义一个协议,该协议实现了两种方法,这些方法有助于Dictionary-fying和un-Dictionary-fying您的结构。使用协议和泛型的优点是,当结构是另一个结构的属性时,它可以工作。这种嵌套可以达到任何深度。
我称协议为“Dictionariable&#39;”,表示符合协议的任何内容都可以转换为字典。定义如下。
protocol Dictionariable {
func dictionaryRepresentation() -> NSDictionary
init?(dictionaryRepresentation: NSDictionary?)
}
现在,考虑一个结构&#39;电影&#39;
struct Movie {
let name: String
let director: String
let releaseYear: Int
}
让我扩展结构并使其符合“Dictionariable”#39;协议
extension Movie: Dictionariable {
func dictionaryRepresentation() -> NSDictionary {
let representation: [String: AnyObject] = [
"name": name,
"director": director,
"releaseYear": releaseYear
]
return representation
}
init?(dictionaryRepresentation: NSDictionary?) {
guard let values = dictionaryRepresentation else {return nil}
if let name = values["name"] as? String,
director = values["director"] as? String,
releaseYear = values["releaseYear"] as? Int {
self.name = name
self.director = director
self.releaseYear = releaseYear
} else {
return nil
}
}
}
基本上,我们现在有办法将结构安全地转换为字典。我说安全是因为我们实现了字典的形成方式,每个结构都是如此。只要该实现是正确的,功能就可以工作。从字典中获取结构的方法是使用可用的初始化器。它必须是可用的,因为文件损坏和其他原因可能使结构的实例化从存档不完整。这种情况可能永远不会发生,但它可以更安全地发挥作用。
func extractStructuresFromArchive<T: Dictionariable>() -> [T] {
guard let encodedArray = NSKeyedUnarchiver.unarchiveObjectWithFile(path()) as? [AnyObject] else {return []}
return encodedArray.map{$0 as? NSDictionary}.flatMap{T(dictionaryRepresentation: $0)}
}
func archiveStructureInstances<T: Dictionariable>(structures: [T]) {
let encodedValues = structures.map{$0.dictionaryRepresentation()}
NSKeyedArchiver.archiveRootObject(encodedValues, toFile: path())
}
//Method to get path to encode stuctures to
func path() -> String {
let documentsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).first
let path = documentsPath?.stringByAppendingString("/Movie")
return path!
}
以上两种方法可以归档和取消归档一组电影&#39;结构体。您需要注意的是实现“Dictionariable”&#39;需要存档的每个结构的协议。
检查out this blogpost我写了比较三种存档和取消存档快速结构的方法。有一个更详细的实现和上面解释的代码的操场文件,您可以在链接中运行和测试。
答案 3 :(得分:0)
除了上面提到的NSCoding不支持结构外,我的建议是你的类也应该从NSObject继承,以便能够对自身及其属性进行编码和解码。