読者です 読者をやめる 読者になる 読者になる

SwiftのPointy Bits | try! Swift Tokyo 2017 #tryswiftconf Day1-3 聞き起こし

twitter.com

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を使用しても安全を保つための秘訣)を説明します。

speakerdeck.com

SwiftのPointy Bits

いろんなポインタがあって、なぜいろいろあるか、どのように使い分けるか

そのまえにSwiftが安全である、とはどういうことかの話をします。

Optionalは安全に大きな役割を果たしています。

f:id:niwatako:20170302114223j:plain

f:id:niwatako:20170302114224j:plain

f:id:niwatako:20170302114225j:plain

f:id:niwatako:20170302114226j:plain

f:id:niwatako:20170302114227j:plain

f:id:niwatako:20170302114238j:plain

f:id:niwatako:20170302114240j:plain

配列が空だとクラッシュしますね

f:id:niwatako:20170302114256j:plain

f:id:niwatako:20170302114306j:plain

平均を求める関数を紹介します

ゼロで割るとクラッシュします

f:id:niwatako:20170302114332j:plain

f:id:niwatako:20170302114338j:plain

f:id:niwatako:20170302114354j:plain

こんな簡単にクラッシュして安全と言って良いのか?

f:id:niwatako:20170302114403j:plain

安全とは、クラッシュからの安全なのか、そうでないものなのか

f:id:niwatako:20170302114417j:plain

プログラムがクラッシュする以外に何があるか??

f:id:niwatako:20170302114435j:plain

クラッシュしなかったらどうなっていたでしょう

f:id:niwatako:20170302114500j:plain

予期せぬ動きから守ってくれていたのです。

Swiftの安全性が担保するより他の形で安全を担保したい時、Unsafeを使うことができます。Unsafeは言語が提供する安全性を放棄して自分で責任を持ちます。

メモリはこうなります

f:id:niwatako:20170302114626j:plain

f:id:niwatako:20170302114645j:plain

メモリのすべての場所にはアドレスが有ります

f:id:niwatako:20170302114705j:plain

各行のアドレスは前のアドレスプラス8ですね

任意の値はメモリに格納されます。

f:id:niwatako:20170302114736j:plain

f:id:niwatako:20170302114745j:plain

f:id:niwatako:20170302114749j:plain

withUnsafePointer関数を使うと直接アドレスを扱えます。

f:id:niwatako:20170302114826j:plain

f:id:niwatako:20170302114844j:plain

f:id:niwatako:20170302114835j:plain

4つのポインタ型、それぞれがメモリのアドレスである

f:id:niwatako:20170302114902j:plain

二つのアクセスで4つのタイプを見る

型がついている

f:id:niwatako:20170302114935j:plain

SwiftはAliasが厳格なので同じメモリに異なる二つのタイプでアクセスすることはできません

f:id:niwatako:20170302115009j:plain

型付きポインタはロケーションタイプを把握している。全体のあたい(連続コンテンツ)を指す。

インスタンスの中央に着陸してしまうようなことはない

型情報がないパターン

f:id:niwatako:20170302115046j:plain

型情報がないただのアドレスになります。バイトで単純にアクセスする。

二つ目の軸はMutabilityです。インスタンスレベルではなくTypeレベルでMutabilityをコントロールする

f:id:niwatako:20170302115200j:plain

SwiftのBuffer Pointer

f:id:niwatako:20170302115218j:plain

コレクションのような振る舞いをしてメモリ上のコンテンツをイテレート。このように扱える。

f:id:niwatako:20170302115251j:plain

2000歳だと

f:id:niwatako:20170302115317j:plain

UnsafePointerTypeをどのような時に使うか

f:id:niwatako:20170302115350j:plain

最適化の種類

f:id:niwatako:20170302115357j:plain

f:id:niwatako:20170302115358j:plain

複雑な例ですがIn・Outを扱っています。

ポインタータイプをいろいろ前置きしましたがUnsafePointerを使う必要はありません

In Out Syntaxを使うだけでいいです

f:id:niwatako:20170302115506j:plain

最初のArrayエレメントのポインタになるので十分なスペースで最大カウントを渡す必要がある。

ドキュメントIDをとっていくとArrayをループオーバーして見つけられたドキュメントの数だけが最大になる。

明示的にUnsafePointerを使う

f:id:niwatako:20170302115640j:plain

安全を保証する2つの箇所

f:id:niwatako:20170302115648j:plain

それぞれのエントリを初期化し0にする。エレメントにアクセスをさせないということで適切な初期化がされていないとアクセスを許さない。SKSearchFindmathcesに渡すことで初期化が不要

別のアプローチ

f:id:niwatako:20170302115731j:plain

プラスアルファのアプローチ

f:id:niwatako:20170302115753j:plain

すべてのパフォーマンスを最大限引き出そうとする時、すべてのアクセスでバウンドチェックが必要かというとそうではない

変換

f:id:niwatako:20170302115829j:plain

これはオプトアウトしている安全機能です。Swiftは適切にできるようにしているので割り当て外しをdefferに入れてくれる。

そしてパラメータからアンパサンドを外す

f:id:niwatako:20170302115900j:plain

これはすべて最適化しました。不要なInitializationやバウンズチェックを外しています。

読み込む前に初期化、allocateしたものをdeallocateする、バウンドに収める。

これはバブルソート

f:id:niwatako:20170302115942j:plain

f:id:niwatako:20170302115952j:plain

ソーティングルーティンを早めたい。あるいはバブルソートを使わない最適化も考えられる。

f:id:niwatako:20170302120031j:plain

UnsafePointerを扱うことでパフォーマンスが得られるし、エレメントの型を保証できる。

もう一つ、ポインタの誤用

f:id:niwatako:20170302120055j:plain

ポインタをエスケープすること

f:id:niwatako:20170302120120j:plain

f:id:niwatako:20170302120140j:plain

f:id:niwatako:20170302120141j:plain

Undefinedな振る舞い。エスケープした後になんでも最適化出来る

f:id:niwatako:20170302120158j:plain

In out を使っているが二つのルールが適用できる。

エスケープしない、Inplicit Conversionで変数のポインタを入手しない。

f:id:niwatako:20170302120235j:plain

Q&A

UnsafeRawPointerのアシュームメモリーバウンドとバウンドメモリの違いは何でしょうか

バインドメモリとアシュームメモリバウンドの違いですね

UnsafePointerはメモリでアシュー蒸しているのは分からないが、別のクラスのタイプなどでバウンドすることが出来る。型を変換するとコンパイラにメモリをバインドする、後ほど再バインドしない限りはそういうふうにする。 アシュームするとチェックをバイパスする。想定して既にバインドしているという風に認識する。RawメモリをアロケートしてメモリをバインドするとUndefinedBehaviorになる。

エスケープすべきでないと言って、その次の行のブロックで使っているがそれもエスケープになるか

なる

Cファンクションは戻ってこない。うっかりポインタをエスケープすることになる。その例は実際Playgroundでも行える。ConputedPropertyでやると想定した機能はしない、 UndefinedBehavior故にあとの最適化をすべて排除してしまいます。それぞれのポインタ=10としたとしても値は0です。

みんなの反応

もっと反応を見る

togetter.com

気に入った記事は はてなブックマーク

はてなブックマークアプリiOS開発チームから来ました!はてなブックマークにはSwift特集があります!良い記事を見逃さないように、ご利用ください!

http://b.hatena.ne.jp/hotentry/it/Swift

そして良い記事があったらはてなブックマークでブックマークしましょう!

try! Swift の記事で盛り上がると嬉しいです!