【Swift】Data型によるUIntについて
はじめに
iOSアプリ開発を実施している際、案件 / 個人開発時に
BLEを用いてデータ処理 / 通信を実施することは少なからずあるかと思います。
私自身、案件にてBLEを使用する機会があったため
今まであまり触れる機会がなかったData
によるUInt
系の扱いについての見解を残していきたいと思います。
UIntとは
まずUIntとは何かから復習したいと思います。
- 公式より -
On 32-bit platforms, UInt is the same size as UInt32, and on 64-bit platforms, UInt is the same size as UInt64.
- 日本語訳 -
32ビットプラットフォームでUIntは、UInt32と同じサイズであり、64ビットプラットフォームでUIntは、UInt64と同じサイズです。
UInt = 符号なし整数値のこと
※要は整数を格納するData型のこと(0 ~ 4,294,967,295
の整数値を使用できる)
Dataとは
- 公式より -
The Data value type allows simple byte buffers to take on the behavior of Foundation objects. You can create empty or pre-populated buffers from a variety of sources and later add or remove bytes. You can filter and sort the content, or compare against other buffers. You can manipulate subranges of bytes and iterate over some or all of them.
- 日本語訳 -
Data値型を使用すると、単純なバイトバッファでFoundationオブジェクトの動作を引き継ぐことができます。さまざまなソースから空または事前入力されたバッファを作成し、後でバイトを追加または削除できます。コンテンツをフィルタリングして並べ替えたり、他のバッファと比較したりできます。バイトのサブ範囲を操作して、それらの一部またはすべてを反復処理できます。
要はData型はバイト列を表現するための型。
※1byte... 8bit
簡単な例
let data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(data) // 10 bytes
let value = data.map {
String(format: "%.2hhx/", $0)
}
.joined()
print(value) // 00/01/02/03/04/05/06/07/08/09/
- 説明 -
let data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(data) // 10 bytes
こちらではDataインスタンス生成時に引き渡しているUIntの整数値のアドレスに
メモリへのポインタを内部で割り当てています。
→[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
そのため結果は10 bytes
となります。
let value = data.map {
String(format: "%.2hhx/", $0)
}
.joined()
print(value) // 00/01/02/03/04/05/06/07/08/09/
上記で割り当てた整数値を16進数文字列に変換 / 結合し出力しています。
※各々1byteに割り当てた整数値が出力されています。
数値リテラル
Swiftの数値リテラルも色々と種類があります。
- 10進数 -
let i = 10
print(i) // 10
人が一番理解しやすい扱い方ですね。
- 2進数 -
let i = 0b1010
print(i) // 10
10の整数値を2進数で表すには0b1010
という形になります。
※0b
は2進数を表すプリフィックスです。16進数では0x
、8進数では0o
となります。
- 16進数 -
let i = 0x0A
print(i) // 10
- 8進数 -
let i = 0o12
print(i) // 10
DataのbyteCodeを取得
前置きが長くなりましたがここからが本題です。
まずはDataインスタンスに割り当てたbyteCodeを取得していきます。
- Data Extension -
extension Data {
var encodedHexadecimals: [UInt8] {
return self.withUnsafeBytes { pointer -> [UInt8] in
guard let address = pointer
.bindMemory(to: UInt8.self)
.baseAddress else { return [] }
return [UInt8](UnsafeBufferPointer(start: address, count: self.count))
}
}
}
- 使用例 -
let data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(data.encodedHexadecimals) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- 説明 -
return self.withUnsafeBytes { pointer -> [UInt8] in
こちらは特定のDataインスタンスのバッファにアクセスする為のメソッドとなります。
記憶してある情報にアクセスする際にはこちらを用います。
guard let address = pointer
.bindMemory(to: UInt8.self)
.baseAddress else { return [] }
return [UInt8](UnsafeBufferPointer(start: address, count: self.count))
アクセスしたバッファに対し、bindMemory
は特定のタイプ(今回はUInt8型)を指定することにより指定したタイプにバインドしたバッファを返してくれます。
baseAddress
はバッファの最初のポインタを取得します。
その取得したポインタを元に配列UInt8を返却しています。
取得したUInt8を10進数に変換
上記の例ではData(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
としていましたが
何かしらの機器などから受け取る際には8進数 / 16進数 / 32進数などで受け取ることがあると思います。
その際に、今回は8進数の数値を取得した際に10進数に変換する方法を記載します。
let data = Data(bytes: [0x00, 0x01, 0x02, 0x10, 0x1B, 0xA1, 0x06
data.forEach {
print(String($0, radix: 10))
}
- 結果 -
0
1
2
16
27
161
6
メチャクチャ簡単です。。
String($0, radix: 10)
今回は10進数なので、引数に10を渡していますが
用途によって16進数に変換したかったら16、8進数だったら8と指定すればよしなに変換してくれます。
※今回は例として記載しましたが、こちらのradix:
はデフォルト10となるので、指定しなくても良いです。
終わりに
今回は簡単な数値リテラルの説明、Dataインスタンスからのbyte列の取得を説明しました。
別途
1byteをbitに変換 / 1bit取得などを実施したい場合は
以下の記事を参考にしていただければと思います。(今回の内容も一部こちらを参考にさせていただいております。)
文系出身の方は最初苦手意識が出てしまいますが
調べてみると簡単なもの、やっぱわからないなと思うものが出てくると思いますが
網羅せずとも先駆者はいらっしゃいますので、そちらを参考にしていきましょう
※理解だけしておけばかなり便利
参考
Discussion