Подтвердить что ты не робот

Архивный массив необязательных структур с NSCoding в Swift?

Я делал много архивации NSCoding в Obj-C, но я не уверен, как он обрабатывает структуры в 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

Может ли кто-нибудь заставить меня двигаться в правильном направлении?

4b9b3361

Ответ 1

Я не уверен, в чем проблема, но если вы используете 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 по какой-либо причине не удается получить массив структуры. - Я уверен, что кто-то еще может дать больше понимания, надеюсь, что это поможет! Swift может быть бурной: D

Ответ 2

В Язык Swift для программирования, Apple заявляет:

Swift предоставляет две специальные псевдонимы типов для работы с неспецифическими типами:
- AnyObject может представлять экземпляр любого типа класса.
- Any может представлять экземпляр любого типа вообще, включая типы функций.

Зная, что тип SquareCoords (Swift Structure) и тип [SquareCoords] (Swift-массив Swift Structure) не могут соответствовать протоколу 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 class (Array, Dictionary, соединяется с NSArray и NSDictionary одинаковым образом). Прочтите этот пост в блоге, если вы хотите лучше посмотреть на него.

Ответ 3

Другие ответы, приведенные здесь, решают вашу проблему. Но недавно я столкнулся с подобной проблемой, пытаясь архивировать мои структуры Swift и разработал интересный способ решить эту проблему. NSCoding не поддерживает структуры.

По существу, следующий метод включает преобразование свойств структуры в словарные элементы. Но он делает это элегантно, используя протоколы. Все, что вам нужно сделать, это определить протокол, который реализует два метода, которые помогают в Словарь-сражающемся и не-словаре, защищающем вашу структуру. Преимущество использования протоколов и дженериков заключается в том, что оно работает, когда структура является свойством другой структуры. Это гнездо может быть на любой глубине.

Я вызываю протокол "Dictionariable" , который указывает, что все, что соответствует протоколу, может быть преобразовано в словарь. Определение имеет следующий вид.

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

Теперь рассмотрим структуру 'Movie'

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

Позвольте мне расширить структуру и привести ее в соответствие с протоколом "Dictionariable" .

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

Два вышеуказанных метода могут архивировать и разблокировать массив структуры "Кино". Все, что вам нужно позаботиться, это реализация протокола "Dictionariable" для каждой из ваших структур, которые необходимо архивировать.

Отметьте этот blogpost Я написал сравнение трех способов архивирования и распаковки быстрых структур. Существует более подробная реализация и файл детской площадки приведенного выше кода, который вы можете запустить и протестировать по ссылке.

Ответ 4

Кроме того, что NSCoding не поддерживает структуры, как указано выше, мое предложение заключается в том, что ваш класс также должен наследовать от NSObject, чтобы иметь возможность кодировать и декодировать себя и свои свойства.