Swiftで安全でない

現代のプログラミング言語の作成者は、そのような機能を言語(Javaなど)に含めないか、怖い言葉で安全でない(C#)としてマークすることにより、プログラマーがポインターやメモリを直接操作することを避けようとしています。 swiftへの書き込みは幸運でした。この言語は2番目のカテゴリに分類されました。これは推奨されませんが、ドキュメントにはメモリリークやその他のホラーストーリーに関する警告が含まれていますが、そのような可能性があります!



この記事では、ポインタを迅速に操作することを検討し、なぜこれが必要なのかを推測したいと思います。



それで、最初の質問はなぜですか?



ほとんどの場合、iOS用のswiftを使用したプログラミングはポインターを使用する必要がありません;さらに、ほとんどのプログラマーはポインターを明示的に使用したことがありません。 それでも、場合によっては、このような低レベルのコードを使用すると、プログラムの速度または使用メモリ量を最適化できます。 また、このような機会は学術的な観点からも興味深いものです。



どうやって?



ポインターの一般的な作業は、メモリの割り当て、割り当てられた領域の操作、およびメモリの解放の3つの手順で構成されます。



例:



let p = UnsafeMutablePointer<Int>.alloc(1) p.initialize(20) p.destroy(1) p.dealloc(1)
      
      





迅速の観点から、ポインターは対応する型(この場合は整数)のジェネリックです

ここでは、Int型にメモリを割り当て、20の値で初期化して解放します。



ただし、この方法で1つの整数値をいじるのは特に有用ではありません。一時配列が必要な場合は別のことです。



 let a = UnsafeMutablePointer<Int32>.alloc(N) memset(a, 0, sizeof(Int32) * N)
      
      





ここでは、サイズNのInt32要素の型で「a」配列にメモリを割り当て、sizeof(Int32)* Nバイトを0に設定して初期化しました。



ここで、コマンドの配列をコピーします。



 var b = a
      
      





迅速な配列とは異なり、コピーされるのはデータそのものではなく、それらへのポインタです。 データは次のようにコピーできます。



 let b = UnsafeMutablePointer<Int32>.alloc(N) memcpy(b, a, sizeof(Int32) * N)
      
      





このような配列の繰り返しは、非常に簡単に実行できます。



 for var i = 0; i < N; i++ { a[i] = <...> }
      
      





そして、数学ポインターを使用できます。



 var p = a while p < a + N { p.memory = <...> p++ }
      
      





(整数をポインターに追加し、対応する要素数を指すアドレスをシフトします)



ただし、かなり退屈な理論なので、ポインターを使用して問題を解決してみましょう。

たとえば、GIFファイルから画像のサイズを取得します。



これを行うには、実験ファイルとフォーマットヘッダーの説明が必要です。

オフセット 長さ 内容
0 3バイト GIF
3 3バイト 「87a」または「89a」
6 2バイト
8 2バイト 身長
... ... ...


まず、GIFファイルを読み取り、読み取りバイトシーケンスの先頭へのポインターを取得します。



 let fileName = NSBundle.mainBundle().pathForResource("SomeGif", ofType: "gif")! let data = NSData(contentsOfFile: fileName)! guard data.length > 10 else { return } var p = UnsafeMutablePointer<Void>(data.bytes)
      
      





したがって、「p」ポインターは、GIFファイルからのデータを含むバッファーの先頭を指し、バッファーサイズが10バイトを超えていることも確認します(これは、これから読み取る量です)。



形式の説明によると、ヘッダーの最初の3バイトは文字列「GIF」である必要があります。確認するには、バッファーの最初の3バイトに基づいてSwift行を作成し、次の項目を確認します。



 let str = String(bytesNoCopy: p, length: 3, encoding: NSASCIIStringEncoding, freeWhenDone: false) guard str == "GIF" else { return }
      
      





したがって、pポインターによって参照される最初の3バイトをASCII文字セットとして解釈するSwift文字列を作成します。最も注目すべきは、バッファーからのデータ自体がコピーされないことです。



さらに、チェックが成功した場合、対象の画像のサイズを読み取ります。 これを行うには、2つの16ビット整数の構造を使用します。



 struct GifSize { var width: UInt16 var height: UInt16 }
      
      





形式に従って、ファイルの先頭から6バイト(以前は文字列として解釈した最初の3バイト)だけシフトし、次の4バイトを2つの16ビット数として解釈する必要があります。



 p += 6 let pSize = UnsafeMutablePointer<GifSize>(p) NSLog("Gif width = \(pSize.memory.width), height = \(pSize.memory.height)")
      
      





したがって、ポインターを使用してサードパーティのライブラリなしでタイトルを読み取ることにより、GIFファイル内の画像のサイズを取得しました。



(一般的に、Swiftでの構造の同様の使用には、いくつかの落とし穴と制限がありますが、それらは別の注意に値します)




All Articles