web-dev-qa-db-ja.com

NSKeyedArchiverは私のCodableクラスをアーカイブしません

これが私のCodableクラスです:

class SensorOutput: Codable {

    var timeStamp: Date?

    var gyroX: Double?
    var gyroY: Double?
    var gyroZ: Double?

    var accX: Double?
    var accY: Double?
    var accZ: Double?

    var magX: Double?
    var magY: Double?
    var magZ: Double?

    init() {}
}

ここでは、そのクラスのオブジェクトをファイルに書き込んだり読み取ったりしようとしています。

    let myData = SensorOutput()
    myData.timeStamp = Date()
    myData.gyroX = 0.0
    myData.gyroY = 0.0
    myData.gyroZ = 0.0
    myData.accX = 0.0
    myData.accY = 0.0
    myData.accZ = 0.0
    myData.magX = 0.0
    myData.magY = 0.0
    myData.magZ = 0.0

    NSKeyedArchiver.archiveRootObject(myData, toFile: filePath)

    if let Data = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? SensorOutput {
        print (Data)
    }

これにより、アーカイブの処理中にエラーが発生します: エラーのスクリーンショット

PS:私がそのような方法で受け取ったfilePath:

var filePath: String {
    //1 - manager lets you examine contents of a files and folders in your app; creates a directory to where we are saving it
    let manager = FileManager.default
    //2 - this returns an array of urls from our documentDirectory and we take the first path
    let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first
    print("this is the url path in the documentDirectory \(String(describing: url))")
    //3 - creates a new path component and creates a new file called "Data" which is where we will store our Data array.
    return (url!.appendingPathComponent("Data").path)
}

読み取り/書き込みは、IntまたはDouble、およびサポートされている他のタイプで機能しますが、私のタイプでは機能しません。どうしましたか?

8

@mattの回答には問題を解決するための重要な情報が含まれていますが、SwiftおよびiOSプログラミングに慣れていない場合は、その情報をどのように適用するかが明確でない場合があります。

クラスメソッドであるNSKeyedArchiver.archiveRootObject(_:toFile:)を使用しようとしたため、NSKeyedArchiverのインスタンスを作成する必要はありませんでした。 encodeEncodable(_:forKey:)はクラスメソッドではなくインスタンスメソッドであるため、使用するにはNSKeyedArchiverのインスタンスを作成する必要があります。また、アーカイバがバイトを追加するためにNSMutableDataを作成する必要があり、オブジェクトをエンコードした後にfinishEncodingを呼び出す必要があります。

_    let sensorOutput = SensorOutput()
    sensorOutput.timeStamp = Date()

    let mutableData = NSMutableData()
    let archiver = NSKeyedArchiver(forWritingWith: mutableData)
    try! archiver.encodeEncodable(sensorOutput, forKey: NSKeyedArchiveRootObjectKey)
    archiver.finishEncoding()

    // You can now write mutableData to a file or send it to your server
    // or whatever.
_

同様に、クラスメソッドであるNSKeyedUnarchiver.unarchiveObject(withFile:)を使用しようとしましたが、インスタンスメソッドであるdecodeDecodable(_:forKey:)またはdecodeTopLevelDecodable(_:forKey:)を使用する必要があります。したがって、アーカイブデータを読み込み、それを使用してNSKeyedUnarchiverのインスタンスを作成する必要があります。

_// Read in the data from a file or your server or whatever.
// I'll just make an immutable copy of the archived data for this example.
let data = mutableData.copy() as! Data

let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
do {
    if let sensorOutputCopy = try unarchiver.decodeTopLevelDecodable(SensorOutput.self, forKey: NSKeyedArchiveRootObjectKey) {
        print("deserialized sensor output: \(sensorOutputCopy)")
    }
} catch {
    print("unarchiving failure: \(error)")
}
_

(アーカイブが破損している場合にクラッシュする代わりにSwiftエラー)をスローするため、decodeTopLevelDecodableではなくdecodeDecodableメソッドを使用します。)

12
rob mayoff

エラーメッセージは、SensorOutputがNSObjectから派生する必要があることを示しています。

ただし、根本的な問題は、NSKeyedArchiverを間違って使用していることです。このタイプがNSCodingを採用したかのように動作して、archiveRootObjectを呼び出しています。そうではありません。 Codableを採用しています。このタイプがCodableであるという事実を使用する場合は、NSKeyedArchiver encodeEncodable(_:forKey:)を呼び出してエンコードし、NSKeyedUnarchiver decodeDecodable(_:forKey:)を呼び出してデコードします。

8
matt

あなたはそれを間違った方法で使っていました。 CodableプロトコルではなくNSCodingプロトコルを採用しています。 (@ mattが指摘したように)

あなたはそれをそのように解決することができます:

let myData = SensorOutput()
let data = try! PropertyListEncoder().encode(myData)
NSKeyedArchiver.archivedData(withRootObject: data)
0
Jakub Truhlář