[解決済み] ラウンドトリップスウィフト番号の種類は、データとの間で
質問
Swift 3が傾きつつある中
Data
の代わりに
[UInt8]
私は、データオブジェクトとして様々な数値型(UInt8、Double、Float、Int64など)をエンコード/デコードする最も効率的で慣用的な方法を見つけ出そうとしています。
そこには UInt8]を使用するためのこの答えがあります。 がありますが、Dataで見つからない様々なポインタAPIを使用しているようです。
基本的には以下のようなカスタムエクステンションを使いたいと思います。
let input = 42.13 // implicit Double
let bytes = input.data
let roundtrip = bytes.to(Double) // --> 42.13
ドキュメントの束に目を通しましたが、本当に理解できない部分は、基本的な構造体(すべての数値がそうです)からある種のポインタ(OpaquePointer、BufferPointer、UnsafePointerなど)をどうやって取得できるかということです。C では、その前にアンパサンドを置くだけで、それで十分です。
どのように解決するのですか?
注意してください。 のコードが更新されました。 Swift 5 (Xcode 10.2)に対応しました。(Swift 3とSwift 4.2のバージョンは編集履歴にあります。) また、不整列なデータも正しく扱えるようになりました。
の作成方法
Data
を作る方法
Swift 4.2 では、データは値から単純に
let value = 42.13
let data = withUnsafeBytes(of: value) { Data($0) }
print(data as NSData) // <713d0ad7 a3104540>
説明
-
withUnsafeBytes(of: value)
は、値の生バイトをカバーするバッファポインタを持つクロージャを呼び出します。 -
生のバッファポインタはバイト列であり、したがって
Data($0)
を使用することができます。
から値を取得する方法
Data
Swift 5の時点では
withUnsafeBytes(_:)
の
Data
は、クロージャを "型なし "で呼び出します。
UnsafeMutableRawBufferPointer
をバイトに変換します。このとき
load(fromByteOffset:as:)
メソッドはメモリから値を読み取ります。
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
let value = data.withUnsafeBytes {
$0.load(as: Double.self)
}
print(value) // 42.13
この方法には一つ問題があります。それは、メモリがプロパティ
アラインメント
であることが必要です(ここでは、8 バイトのアドレスにアラインされています)。しかし、これは保証されていません。例えば、データが別の
Data
値のスライスとして取得された場合などです。
したがって、より安全なのは をコピーする をコピーする方が安全です。
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
var value = 0.0
let bytesCopied = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
assert(bytesCopied == MemoryLayout.size(ofValue: value))
print(value) // 42.13
説明
-
withUnsafeMutableBytes(of:_:)
は、値の生バイトをカバーするミュータブルバッファポインタを持つクロージャを呼び出します。 -
は
copyBytes(to:)
のメソッドでDataProtocol
(これに対してData
に準拠する) は、データからそのバッファにバイトをコピーします。
の戻り値は
copyBytes()
の戻り値は、コピーされたバイト数である。これはコピー先バッファのサイズと同じであり、データに十分なバイト数が含まれていない場合は、それ以下となる。
一般的な解決策その1
上記の変換は、簡単に
struct Data
:
extension Data {
init<T>(from value: T) {
self = Swift.withUnsafeBytes(of: value) { Data($0) }
}
func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
var value: T = 0
guard count >= MemoryLayout.size(ofValue: value) else { return nil }
_ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
return value
}
}
制約条件
T: ExpressibleByIntegerLiteral
は、値を簡単に「ゼロ」に初期化できるようにするためにここに追加されます。このメソッドはとにかく「3値」(整数および浮動小数点)型で使用できるため、実際には制限ではありません。
例
let value = 42.13 // implicit Double
let data = Data(from: value)
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = data.to(type: Double.self) {
print(roundtrip) // 42.13
} else {
print("not enough data")
}
同様に
配列
を
Data
といった具合です。
extension Data {
init<T>(fromArray values: [T]) {
self = values.withUnsafeBytes { Data($0) }
}
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
_ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
return array
}
}
例
let value: [Int16] = [1, Int16.max, Int16.min]
let data = Data(fromArray: value)
print(data as NSData) // <0100ff7f 0080>
let roundtrip = data.toArray(type: Int16.self)
print(roundtrip) // [1, 32767, -32768]
一般的な解決策その2
上記のアプローチには1つの欠点があります:それは実際に整数や浮動小数点型のような "trivial"
のような複雑な型では動作しません。
Array
と
String
は、基礎となるストレージへの(隠れた)ポインタを持っているので
構造体そのものをコピーするだけでは渡せません。また
また、実際のオブジェクト ストレージへの単なるポインターである参照型でも動作しません。
そこで、この問題を解決するために、1つの方法として
-
に変換するための方法を定義したプロトコルを定義します。
Data
に変換し、元に戻すためのメソッドを定義するプロトコルを定義します。protocol DataConvertible { init?(data: Data) var data: Data { get } }
-
プロトコル拡張のデフォルトメソッドとして変換を実装します。
extension DataConvertible where Self: ExpressibleByIntegerLiteral{ init?(data: Data) { var value: Self = 0 guard data.count == MemoryLayout.size(ofValue: value) else { return nil } _ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} ) self = value } var data: Data { return withUnsafeBytes(of: self) { Data($0) } } }
を選択しました。 失敗しやすい このイニシャライザは、提供されたバイト数が型のサイズと一致するかどうかをチェックします。 が型のサイズと一致するかどうかをチェックします。
-
に安全に変換できるすべての型への適合性を宣言します。
Data
に安全に変換できる全ての型への適合性を宣言します。extension Int : DataConvertible { } extension Float : DataConvertible { } extension Double : DataConvertible { } // add more types here ...
これにより、変換がさらにエレガントになります。
let value = 42.13
let data = value.data
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = Double(data: data) {
print(roundtrip) // 42.13
}
2番目のアプローチの利点は、安全でない変換を不用意に行うことができないということです。デメリットは、すべての "safe" 型を明示的にリストアップする必要があることです。
のような自明でない変換を必要とする他の型のためにプロトコルを実装することもできます。
extension String: DataConvertible {
init?(data: Data) {
self.init(data: data, encoding: .utf8)
}
var data: Data {
// Note: a conversion to UTF-8 cannot fail.
return Data(self.utf8)
}
}
または、独自の型に変換メソッドを実装して、値のシリアライズとデシリアライズを行うために必要なことを行う。 値をシリアライズ、デシリアライズするために必要なことを行うために、独自の型に変換メソッドを実装します。
バイトオーダー
上記の方法では、バイトオーダーの変換は行われず、データは常にホスト側のバイトオーダーで であり、データは常にホストのバイトオーダーです。プラットフォームに依存しない表現 (例えば 「ビッグエンディアン」別名「ネットワーク」バイトオーダー) については、対応する整数型 プロパティとイニシャライザを使います。例えば
let value = 1000
let data = value.bigEndian.data
print(data as NSData) // <00000000 000003e8>
if let roundtrip = Int(data: data) {
print(Int(bigEndian: roundtrip)) // 1000
}
もちろん,この変換は一般的な方法で行うこともできます。 変換メソッドで行うこともできます。
関連
-
[解決済み] SwiftからObjective-Cのコードを呼び出すにはどうしたらいいですか?
-
[解決済み] 2倍値を小数点以下x桁に丸める処理を素早く行う。
-
[解決済み] Swiftで乱数を生成する方法とは?
-
[解決済み] Swiftを使用してアプリのバージョンとビルド番号を取得するにはどうすればよいですか?
-
[解決済み] SwiftでURLから画像を読み込む/ダウンロードする
-
[解決済み】Swiftで配列から重複する要素を削除する
-
[解決済み】iOSでSwiftからUUIDを生成する方法
-
[解決済み】swiftで辞書のキーから配列にする
-
[解決済み] ""内の二重引用符を印刷するには?
-
[解決済み] SwiftUIのForEachでインデックスを取得する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
swift 4.0でのdispatch_async,dispatch_afterの使用について
-
[解決済み] 非'@objc'メソッドは'@objc'プロトコルのオプション要件を満たしていない
-
[解決済み] Swift: PREPROCESSORフラグ(`#if DEBUG`など)を使ってAPIキーを実装する方法とは?
-
[解決済み] Swift: インデックスで文字列配列を置換する
-
[解決済み] プロパティの型が内部型を使用しているため、publicを宣言することができない
-
[解決済み] SwiftUIで条件付きでビューを使用する
-
[解決済み] SwiftでNSDatesの秒数の差を整数値で求める。
-
[解決済み] swift で文字列から数字以外の文字を削除する
-
[解決済み] カスタムイニシャライザを持つSwift enumはrawValueイニシャライザを失う
-
[解決済み] Swiftの網羅的なSwitchステートメントのためのNoop