Swift offers remarkable performance while still providing safety through strong types, value semantics, and automatic memory management. For those times when you need to step outside those boundaries, however, Swift also offers tools to directly allocate and manipulate memory. This talk will explore the ins and outs of Swift’s take on pointers: typed and raw pointers and buffers, implicit bridging and casting, and some tips on how to stay safe while using unsafe APIs.
Swiftは、強い型付け、値セマンティクス、および自動メモリ管理により、安全性を提供しながらも優れたパフォーマンスを提供しています。これらの境界の外に出なければいけない場合でも、Swiftはメモリを直接割り当てて操作するツールも提供しています。 このトークでは、Swiftのポインタについてのすべて(型付けされたポインタと生のポインタとバッファ、暗黙的なブリッジングとキャスト、および安全でないAPIを使用しても安全を保つための秘訣)を説明します。
SwiftのPointy Bits
いろんなポインタがあって、なぜいろいろあるか、どのように使い分けるか
そのまえにSwiftが安全である、とはどういうことかの話をします。
Optionalは安全に大きな役割を果たしています。
配列が空だとクラッシュしますね
平均を求める関数を紹介します
ゼロで割るとクラッシュします
こんな簡単にクラッシュして安全と言って良いのか?
安全とは、クラッシュからの安全なのか、そうでないものなのか
プログラムがクラッシュする以外に何があるか??
クラッシュしなかったらどうなっていたでしょう
予期せぬ動きから守ってくれていたのです。
Swiftの安全性が担保するより他の形で安全を担保したい時、Unsafeを使うことができます。Unsafeは言語が提供する安全性を放棄して自分で責任を持ちます。
メモリはこうなります
メモリのすべての場所にはアドレスが有ります
各行のアドレスは前のアドレスプラス8ですね
任意の値はメモリに格納されます。
withUnsafePointer関数を使うと直接アドレスを扱えます。
4つのポインタ型、それぞれがメモリのアドレスである
二つのアクセスで4つのタイプを見る
型がついている
SwiftはAliasが厳格なので同じメモリに異なる二つのタイプでアクセスすることはできません
型付きポインタはロケーションタイプを把握している。全体のあたい(連続コンテンツ)を指す。
インスタンスの中央に着陸してしまうようなことはない
型情報がないパターン
型情報がないただのアドレスになります。バイトで単純にアクセスする。
二つ目の軸はMutabilityです。インスタンスレベルではなくTypeレベルでMutabilityをコントロールする
SwiftのBuffer Pointer
コレクションのような振る舞いをしてメモリ上のコンテンツをイテレート。このように扱える。
2000歳だと
UnsafePointerTypeをどのような時に使うか
最適化の種類
複雑な例ですがIn・Outを扱っています。
ポインタータイプをいろいろ前置きしましたがUnsafePointerを使う必要はありません
In Out Syntaxを使うだけでいいです
最初のArrayエレメントのポインタになるので十分なスペースで最大カウントを渡す必要がある。
ドキュメントIDをとっていくとArrayをループオーバーして見つけられたドキュメントの数だけが最大になる。
明示的にUnsafePointerを使う
安全を保証する2つの箇所
それぞれのエントリを初期化し0にする。エレメントにアクセスをさせないということで適切な初期化がされていないとアクセスを許さない。SKSearchFindmathcesに渡すことで初期化が不要
別のアプローチ
プラスアルファのアプローチ
すべてのパフォーマンスを最大限引き出そうとする時、すべてのアクセスでバウンドチェックが必要かというとそうではない
変換
これはオプトアウトしている安全機能です。Swiftは適切にできるようにしているので割り当て外しをdefferに入れてくれる。
そしてパラメータからアンパサンドを外す
これはすべて最適化しました。不要なInitializationやバウンズチェックを外しています。
読み込む前に初期化、allocateしたものをdeallocateする、バウンドに収める。
これはバブルソート
ソーティングルーティンを早めたい。あるいはバブルソートを使わない最適化も考えられる。
UnsafePointerを扱うことでパフォーマンスが得られるし、エレメントの型を保証できる。
もう一つ、ポインタの誤用
ポインタをエスケープすること
Undefinedな振る舞い。エスケープした後になんでも最適化出来る
In out を使っているが二つのルールが適用できる。
エスケープしない、Inplicit Conversionで変数のポインタを入手しない。
Q&A
バインドメモリとアシュームメモリバウンドの違いですね
UnsafePointerはメモリでアシュー蒸しているのは分からないが、別のクラスのタイプなどでバウンドすることが出来る。型を変換するとコンパイラにメモリをバインドする、後ほど再バインドしない限りはそういうふうにする。 アシュームするとチェックをバイパスする。想定して既にバインドしているという風に認識する。RawメモリをアロケートしてメモリをバインドするとUndefinedBehaviorになる。
エスケープすべきでないと言って、その次の行のブロックで使っているがそれもエスケープになるか
なる
Cファンクションは戻ってこない。うっかりポインタをエスケープすることになる。その例は実際Playgroundでも行える。ConputedPropertyでやると想定した機能はしない、 UndefinedBehavior故にあとの最適化をすべて排除してしまいます。それぞれのポインタ=10としたとしても値は0です。
みんなの反応
驚くほどわかりやすいメモリとポインタの話 #tryswiftconf
— takasek (@takasek) 2017年3月2日
The reason why Array's subscript clashes is described here. https://t.co/l7YCk7Licx #tryswiftconf
— koher (@koher) 2017年3月2日
with...系で得られるポインターは、そのクロージャ内だけで扱いましょう #tryswiftconf
— Syo Ikeda / いけしょー (@ikesyo) 2017年3月2日
withUnsafeMutablePointer知らなかった。明示的にmallocしてから使ってた #tryswiftconf
— ぎぎにゃん (@giginet) 2017年3月2日
UnsafePointerでソートのパフォーマンス上げるのおもしろそう #tryswiftconf
— haranicle (@haranicle) 2017年3月2日
let a = A() // A has `var age`
— ezura (@eduraaa) 2017年3月2日
let agePointer = UnsafeMutablePointer(&a.age)
agePointer.pointee = 100
a.age // 1#tryswiftconf
こんなん https://t.co/AvykgQdQ4G #swift #tryswiftconf pic.twitter.com/yWholqcQii
— ezura (@eduraaa) 2017年3月2日
で!これが面白いんですよね。didSet を消すと即時書き換えに変わるの。最適化されるみたい。でも原則的には Write-Back のはず。 https://t.co/iEa5MamQ3h #swift #tryswiftconf pic.twitter.com/XSZQvXbq7k
— 熊谷 友宏 (@es_kumagai) 2017年3月2日
もっと反応を見る
気に入った記事は はてなブックマーク
はてなブックマークアプリiOS開発チームから来ました!はてなブックマークにはSwift特集があります!良い記事を見逃さないように、ご利用ください!
http://b.hatena.ne.jp/hotentry/it/Swift
そして良い記事があったらはてなブックマークでブックマークしましょう!
try! Swift の記事で盛り上がると嬉しいです!