Arrays 在Swift中使用NSCoding存档可选结构的数组?

Arrays 在Swift中使用NSCoding存档可选结构的数组?,arrays,struct,swift,nscoding,optional,Arrays,Struct,Swift,Nscoding,Optional,我在Obj-C中做了很多NSCoding归档工作,但我不确定它如何处理Swift中的结构,也不确定它如何处理具有可选值的数组。这是我的密码: public struct SquareCoords { var x: Int, y: Int } 这是我需要存储的类: public class Player: NSCoding { var playerNum: Int var name = "" private var moveHistory: [SquareCoor

我在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")
    }
...
在编码器初始化的最后一行,我在XCode中得到了以下错误消息:

'AnyObject' is not convertible to [SquareCoords?]'
在encodeWithEncoder的最后一行:

Extra argument 'forKey' in call

有人能让我朝着正确的方向前进吗?

我不确定确切的问题是什么,但如果您使用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数组

我进一步研究了这个问题,发现它可能与struct的使用有关。(您正在创建一个值类型的结构数组。)这有点猜测,但我注意到,如果类类型用于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")
    }
}
可能由于某种原因,任何对象的强制转换都无法转换为结构数组。-我相信其他人可以提供更多的见解,希望这能有所帮助!斯威夫特可以是狂暴的:D

在苹果州:

Swift为处理非特定类型提供了两个特殊类型别名:
-
AnyObject
可以表示任何类类型的实例。
-
Any
可以表示任何类型的实例,包括函数类型

知道了这一点,类型
SquareCoords
(Swift结构)和类型
[SquareCoords]
(Swift结构的Swift数组)不能符合协议
任何对象

另一方面,
decodeObjectForKey:
需要一个符合协议
AnyObject
的参数,
encodeObject:forKey:
返回
AnyObject
。因此,以下两行无法编译:

moveHistory = aDecoder.decodeObjectForKey("moveHistoryKey") as [SquareCoords?]
aCoder.encodeObject(moveHistory, forKey: "moveHistoryKey")
因此,除非您找到一种方法使
SquareCoords
符合协议
AnyObject
(我不知道这是否可行),否则您必须将
SquareCoords
从Swift结构转换为类

PS:
在这一点上,您可能会问:“好吧,但是,类型<代码>字符串——实际上是一个快捷的结构——怎么可能符合协议<代码>任何对象< /代码>?“嗯,那是因为<代码>字符串< /代码>被无缝地连接到基金会的代码> NString < /Cord>类。(
Array
Dictionary
以相同的方式桥接到
NSArray
NSDictionary
)。如果你想更好地了解它,请阅读。

除了NSCoding不支持上面提到的结构之外,我的建议是,你的类也应该继承NSObject,以便能够对自身及其属性进行编码和解码。

这里给出的其他答案解决了你的问题。但是,我最近在le试图归档我的Swift结构,并想出了一个有趣的方法来解决这个问题。NSCoding不支持结构

从本质上讲,以下方法涉及将结构属性转换为字典元素。但是,它可以优雅地使用协议来实现这一点。您只需定义一个协议,该协议实现两种方法,这两种方法有助于对结构进行字典化和取消字典化。使用协议和泛型的优势在于当一个结构是另一个结构的属性时工作。这种嵌套可以达到任何深度

我将该协议称为“Dictionarable”,这表示任何符合该协议的内容都可以转换为字典

protocol Dictionariable {
    func dictionaryRepresentation() -> NSDictionary
    init?(dictionaryRepresentation: NSDictionary?)
}

现在,考虑一个结构“电影”

struct Movie {
    let name: String
    let director: String
    let releaseYear: Int
}
让我扩展这个结构,使它符合“字典化”协议

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
        }
    }
}
基本上,我们现在有一种方法可以安全地将一个结构转换为一个字典。我说安全是因为我们实现了字典是如何形成的,每个结构都是独立的。只要这个实现是正确的,功能就可以工作。从字典中恢复结构的方法是使用一个失败的初始化器。它必须是fai标签,因为文件损坏和其他原因可能会使存档中的结构实例化不完整。这可能永远不会发生,但更安全的是,它是可失败的

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!
}
func extracturesformarchive()->[T]{
guard let encodedArray=NSKeyedUnarchiver.unarchiveObjectWithFile(path())为?[AnyObject]else{return[]}
返回encodedArray.map{$0 as?NSDictionary}.flatMap{T(dictionaryRepresentation:$0)}
}
func archiveStructureInstances(结构:[T]){
让encodedValues=structures.map{$0.dictionaryRepresentation()}
归档对象(encodedValue,toFile:path())
}
//方法来获取将结构编码到的路径
func path()->字符串{
让documentsPath=NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory,NSSearchPathDomainMask.UserDomainMask,true)
让path=documentsPath?.stringByAppendingString(“/Movie”)
返回路径!
}
上述两种方法可以存档和取消存档一系列“电影”结构。您需要注意的是对每个需要存档的结构实施“可词典化”协议


检查我写的比较存档和非存档swift结构的三种方法的代码。上面解释的代码有一个更详细的实现和一个游乐场文件,您可以在链接中运行和测试。

强制转换失败的原因是任何对象只能是类的实例,而不是结构/枚举/元组。@Lance谢谢!-如果我理解正确解码ObjectForkey返回一个AnyObject,而我们不能将其转换为一个Swift结构数组?--你认为处理这个问题的最佳方法是什么?--使用NSMutableArray?还是使用类而不是结构?@John谢谢……这让我找到了正确的解决方法!我现在已经确定非移动(玩家运行)