it-swarm.com.de

array von optionalen Strukturen mit NSCoding in Swift archivieren?

Ich habe eine Menge NSCoding-Archivierung in Obj-C durchgeführt, bin mir aber nicht sicher, wie es mit Strukturen in Swift oder mit Arrays mit optionalen Werten umgeht. Hier ist mein Code:

public struct SquareCoords {
    var x: Int, y: Int
}

und hier ist die Klasse, die ich speichern muss:

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")
    }
...

In der letzten Zeile des Coder Init wird folgende Fehlermeldung in XCode angezeigt:

'AnyObject' is not convertible to [SquareCoords?]'

und in der letzten Zeile von encodeWithEncoder:

Extra argument 'forKey' in call

Kann mich jemand in die richtige Richtung bringen?

19
Chuck Smith

Ich bin nicht sicher, was das genaue Problem ist, aber wenn Sie NSMutableArray anstelle eines Swift-Arrays verwenden, wird das Problem gelöst:

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")
    }
}

Es scheint so zu sein, dass aDecoder.decodeObjectForKey ein implizit nicht verpacktes AnyObject zurückgibt, das nicht in ein SquareCoords-Array umgewandelt wird.

Ich habe ein bisschen weiter damit gespielt und ich habe bemerkt, dass es etwas mit der Verwendung einer Struktur zu tun hat. (Sie erstellen ein Array von Strukturen, die Werttypen sind.) Dies ist eine Vermutung, aber ich habe festgestellt, dass ein Klassentyp für SquareCoords verwendet wird. Es gibt kein Problem, z.

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")
    }
}

Möglicherweise schlägt die Umwandlung von AnyObject aus irgendeinem Grund in einem Strukturarray fehl. - Ich bin sicher, dass jemand anderes mehr Einblick geben kann, hoffe das hilft etwas! Schnell kann stürmisch sein: D

10
Woodstock

In The Swift Programming Language gibt Apple an:

Swift bietet zwei spezielle Typ-Aliase für die Arbeit mit nicht spezifischen Typen:
- AnyObject kann eine Instanz eines beliebigen Klassentyps darstellen.
- Any kann eine Instanz eines beliebigen Typs darstellen, einschließlich Funktionstypen.

Wenn Sie wissen, geben Sie SquareCoords (Swift Structure) und Typ [SquareCoords] (Swift-Array von Swift Structure) das Protokoll AnyObject nicht ein.

Auf der anderen Seite erfordert decodeObjectForKey: einen Parameter, der dem Protokoll AnyObject entspricht, und encodeObject:forKey: gibt AnyObject zurück. Daher können die beiden folgenden Zeilen nicht kompiliert werden:

moveHistory = aDecoder.decodeObjectForKey("moveHistoryKey") as [SquareCoords?]
aCoder.encodeObject(moveHistory, forKey: "moveHistoryKey")

Daher müssen Sie SquareCoords von Swift Structure in Class umwandeln, es sei denn, Sie finden einen Weg, um AnyObject an das Protokoll SquareCoords anzupassen (ich weiß nicht, ob dies möglich ist).

PS: An dieser Stelle können Sie fragen: "OK, aber wie ist es möglich, dass der Typ String - das ist tatsächlich eine Swift-Struktur - dem Protokoll AnyObject entsprechen kann?" Das liegt daran, dass String nahtlos mit der Foundation-Klasse NSString verbunden ist (Array, Dictionary werden auf dieselbe Weise mit NSArray und NSDictionary verbunden). Lesen Sie diesen Blogbeitrag , wenn Sie einen besseren Blick darauf werfen möchten.

27
Imanou Petit

Die anderen Antworten lösen Ihr Problem. In letzter Zeit hatte ich jedoch ein ähnliches Problem, als ich versuchte, meine Swift-Strukturen zu archivieren, und fand einen interessanten Lösungsweg. NSCoding unterstützt keine Strukturen.

Im Wesentlichen umfasst die folgende Methode das Konvertieren der Struktureigenschaften in Wörterbuchelemente. Dies geschieht jedoch elegant unter Verwendung von Protokollen. Alles was Sie tun müssen, ist ein Protokoll zu definieren, das zwei Methoden implementiert, die das Dictionary-Fying und das Un-Dictionary-Fying Ihrer Struktur unterstützen. Die Verwendung von Protokollen und Generika hat den Vorteil, dass sie funktioniert, wenn eine Struktur eine Eigenschaft einer anderen Struktur ist. Diese Verschachtelung kann bis zu einer beliebigen Tiefe erfolgen.

Ich nenne das Protokoll "Dictionariable", was bedeutet, dass alles, was dem Protokoll entspricht, in ein Wörterbuch umgewandelt werden kann. Die Definition geht wie folgt vor.

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

Betrachten wir nun eine Struktur 'Film'

struct Movie {
    let name: String
    let director: String
    let releaseYear: Int
}

Lassen Sie mich die Struktur erweitern und an das 'Dictionariable'-Protokoll anpassen.

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
        }
    }
}

Grundsätzlich haben wir jetzt eine Möglichkeit, eine Struktur sicher in ein Wörterbuch umzuwandeln. Ich sage sicher, weil wir implementieren, wie das Wörterbuch gebildet wird, individuell für jede Struktur. Solange diese Implementierung stimmt, würde die Funktionalität funktionieren. Um die Struktur aus dem Wörterbuch zurückzuholen, verwenden Sie einen fehlgeschlagenen Initialisierer. Es muss fehlgeschlagen sein, da Dateibeschädigungen und andere Gründe die Instanziierung der Struktur aus einem Archiv unvollständig machen könnten. Das kann nie passieren, aber es ist sicherer, dass es ausfallen kann.

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!
}

Die beiden obigen Methoden können ein Array mit 'Movie'-Struktur archivieren und aufheben. Sie müssen nur darauf achten, dass das Protokoll 'Dictionariable' für jede Ihrer zu archivierenden Strukturen implementiert wird.

Check out this blogpost Ich schrieb drei Vergleiche, um Swift-Strukturen zu archivieren und aus ihnen zu entfernen. Es gibt eine detailliertere Implementierung und eine Spielplatzdatei des oben erläuterten Codes, die Sie im Link ausführen und testen können.

8

Abgesehen davon, dass NSCoding keine Strukturen unterstützt, wie oben erwähnt, ist mein Vorschlag, dass Ihre Klasse auch von NSObject erben sollte, um sich selbst und seine Eigenschaften codieren und decodieren zu können.

0
JohnnyHockey