203 Views
June 15, 15
スライド概要
今回はちょっととりとめのないおまけのお話です。
第 60 回 Cocoa 勉強会関西で発表した内容について、いろいろ教えて頂いて、それを受けて考えていたら思いがけない発見があったりとかして、そんな風にして見えてきた事柄をややまとまりなく綴ってみました。
2015/04/11 当時に見つけた Swift 1.2 までのお話です。当時は Swift 2.0 については影も形もありませんでした。
※ Docswell での公開に移行する直前の Slideshare での閲覧数は 1,719 でした。
正統派趣味人プログラマー。プログラミングとは幼馴染です。
Swift カジュアルプログラミング 《基礎》不不変値と可変値 〜~ 前回の続き(おまけ)〜~ EZ-‐‑‒NET 熊⾕谷友宏 http://ez-‐‑‒net.jp/ 2015.04.11 @ 第61回 Cocoa 勉強会関⻄西
熊谷友宏 http://ez-net.jp/ @es̲kumagai Xcode 5 徹底解説 IP Phone 音でダイヤル 音で再配達ゴッド いつもの電卓 for iPhone いつもの電卓 for iPad 音で再配達
熊谷友宏 http://ez-net.jp/ @es̲kumagai 開催中 #yidev 横浜 iPhone 開発者勉強会
前回 不変値と可変値 Swift にある変数のお話
今回 前回に頂いた質問から見えた いろいろな事柄
前回の内容 ▶ 不変値変数と可変値変数 ▶ 変数は常にコピー ▶ 値に特化した構造体
まずは訂正
訂正 構造体 変数に特化したオブジェクト
訂正 構造体 ▶ データ型? ▶ オブジェクトとは言わない?
訂正 オブジェクト ▶ オブジェクト指向で 手続きの対象を抽象化する概念 ▶ クラス型のインスタンス ▶ Swift では AnyObject で表現
訂正 型 Type 実体 Instance クラス型 クラス オブジェクト データ型 構造体 データ
整理すると ▶ 型は設計図 - class は クラス型 を設計するもの - struct は データ型 を設計するもの ▶ 型からインスタンスを作る - クラス型から オブジェクト ができる - データ型から データ ができる ▶ データを 値 や 状態 として使う
AnyObject
AnyObject ▶ プロトコルのひとつ ▶ 全てのクラスが暗黙的に準拠 ▶ 明示的には準拠させられない
AnyObject 該当するもの // クラス let obj:AnyObject = MyClass()
AnyObject 該当しないもの // 構造体 let obj:AnyObject = MyStruct() // 整数(構造体) let obj:AnyObject = Int(10) // 文字列(構造体) let obj:AnyObject = String("STR") // 列挙型 let obj:AnyObject = MyEnum.A
AnyObject 該当しないもの // 関数 let obj:AnyObject = reduce // クロージャー let obj:AnyObject = { $0 + $1 }
AnyObject 該当するもの // クラスの型 let obj:AnyObject = MyClass.self 該当しないもの // 構造体の型 let obj:AnyObject = MyStruct.self
AnyObject 意味 オブジェクトの概念を持っているもの 対象 ▶ MyClass (クラスの実体) ▶ MyClass.Type (クラスの型)
AnyObject ところが プロジェクト内のどこかで Foundation をインポートすると…
AnyObject 該当するものが変化 // 構造体 let obj:AnyObject = MyStruct() // 整数(構造体) let obj:AnyObject = Int(10) // 文字列(構造体) let obj:AnyObject = String("STR") // 列挙型 let obj:AnyObject = MyEnum.A
AnyObject なぜ Int や String などの 構造体 が AnyObject になるのか
AnyObject Foundation の中に理由があるはず でも、それを探るのは困難そう NSString や NSNumber が怪しいのですが…
そういえば 前回の懇親会での一幕
Objective-C Bridge String と NSString って どうやってブリッジしているの?
Objective-C Bridge ̲ObjectiveCBridgeable
Objective-C Bridge STEP 1/7 ▶ ブリッジさせたい 構造体 を ̲ObjectiveCBridgeable に準拠 struct MyStruct:_ObjectiveCBridgeable { }
Objective-C Bridge STEP 2/7 ▶ ブリッジする対象のクラスを決定 ▶ AnyObject 相当のものを指定 typealias _ObjectiveCType
Objective-C Bridge STEP 3/7 ▶ ブリッジできるかの判定を実装 ▶ 自身の型がブリッジできるかを判定 static func _isBridgedToObjectiveC() -> Bool
Objective-C Bridge STEP 4/7 ▶ ブリッジするクラスの型を返却 ▶ 必ず _ObjectiveCType.self を返却 public static func _getObjectiveCType() -> Any.Type
Objective-C Bridge STEP 5/7 ▶ ブリッジ後のインスタンスを返却 ▶ 自身の情報から新インスタンスを作成 public func _bridgeToObjectiveC() -> _ObjectiveCType
Objective-C Bridge STEP 6/7 ▶ ブリッジ先から自身にブリッジする ▶ as 演算子によるキャスト動作を実装 public static func _forceBridgeFromObjectiveC( source: _ObjectiveCType, inout result: MyObjCBridgeClass? )
Objective-C Bridge STEP 7/7 ▶ ブリッジ先から自身にブリッジする ▶ as? 演算子によるキャスト動作を実装 public static func _conditionallyBridgeFromObjectiveC( source: _ObjectiveCType, inout result: MyObjCBridgeClass? ) -> Bool
Objective-C Bridge ブリッジ可能な構造体の完成 struct MyStruct : _ObjectiveCBridgeable { typealias _ObjectiveCType = NSString } 用途 let structValue = MyStruct() let string = structValue as NSString
Objective-C Bridge Foundation の Objective-C ブリッジも この方法で実装されているのでしょう ▶ String ▶ Int ▶ Array<T> …
もしかして
再び AnyObject
AnyObject この Objective-C ブリッジと 構造体が AnyObject に変わる事象は 関係している?
AnyObject 関係してました。 // 構造体 let obj:AnyObject = MyStruct() ▶ Swift 構造体が AnyObject に準拠 ▶ AnyObject にブリッジできるから?
AnyObject まとめ ▶ AnyObject はプロトコル ▶ 全てのクラスが暗黙的に準拠する ▶ 原則、クラス以外は準拠しない ▶ AnyObject へブリッジできる 構造体は AnyObject に準拠する
本題
値はコピーされる
前回 変数の特徴 ▶ 代入時に内容をコピー ▶ let と var の相互乗り入れを実現 コピー let value = state コピー let state = value
前回 代入時に必ずしも コピーされるとは限らないのでは? たぶん そう思います。
コピーの最適化 たぶん 最適化してくれる
コピーの最適化 最適化が介入できそうな場所は… ▶ コンパイラ、プリプロセッサ 言語仕様の範疇で最適化 ▶ オプティマイザー 最終的な最適化の専門フェーズ ▶ 標準ライブラリ 型の設計によるチューニング
コピーの最適化 Swift の強み ▶ 変数は原則コピー 代入時に暗黙的にコピーされる Swift 暗黙コピー var value:Int = source Objective-C 明示コピー NSMutableNumber* value = source.copy();
コピーの最適化 Swift の強み ▶ 変数は原則コピー 代入時に暗黙的にコピーされる ▶ 不変値と可変値が明確 書き変わらない変数を把握できる 裁量でコピーを省きやすい言語仕様
コピーの最適化 オプティマイザーに期待
Array<T> の最適化
Array<T> の最適化 Array は値型(構造体) ▶ 代入時に暗黙コピー ▶ 全ての要素がコピーの対象 Swift 全要素の暗黙コピー var values:Array<Int> = sources
Array<T> の最適化 要素が多いときの処理速度が心配 設計による最適化が図られている
Array<T> の最適化 Array の最適化 ▶ 要素をバッファーで保持 ▶ 代入時はポインターのコピーで済ませる ▶ 内容の書き換え時にバッファーをコピー 内容が変わらないうちは共有する
Array<T> の最適化 コピーの動作(代入時) コピー 配列 配列 バッファーは共有 Buffer
Array<T> の最適化 コピーの動作(書き換え時) 書き込み! 配列 配列 × Buffer Buffer 遅延コピー
Array<T> の最適化 原則コピーの体をなしつつ 最適化を実現 ▶ 代入時や引数へ渡すときの コピーの負荷は心配無用 ▶ 内容を書き換えるときに負荷が発生
余談
CGImage
CGImage 定義 Objective-C // 実装は隠蔽されているがC構造体 typedef struct CGImage *CGImageRef; Swift // 実装は隠蔽 typealias CGImageRef = CGImage
CGImage 定義 Swift ▶ AnyObject である ▶ ̲ObjectiveCBridgeable ではない ▶ Type は ̲̲NSCFType(詳細不明) クラスを使った実装になっている様子
CGImage Objective-C とは違った方法で 最適化が図られていそう
let の遅延初期化
let の遅延初期化 概要 ▶ Swift 1.2 の新機能 ▶ 不変値変数の宣言と初期化を分離可能 Swift let image:UIImage if condition { image = UIImage(named: "maru.png") } else { image = UIImage(named: "batsu.png") }
let の遅延初期化 欠点 ▶ 宣言と初期化の分離 ▶ コードを追わないと値が分からない Swift let image:UIImage if condition { image = UIImage(named: "maru.png") } else { image = UIImage(named: "batsu.png") }
let の遅延初期化 活用できそうな場面がありました。 再帰呼び出し可能なクロージャーを let だけで実現したい場面
let の遅延初期化 Swift 1.1 時代 再帰呼び出し可能なクロージャー ▶ let だけで定義すると未初期化エラー
let の遅延初期化 Swift 1.1 までは 再帰呼び出し可能なクロージャーを let だけで実現しようとすると… ➡ 不動点コンビネーター?
let の遅延初期化 不動点コンビネーター f(g(f)) = g(f) 関数 f を引数に取る関数 g を 次の式を満たすように作る …らしい
let の遅延初期化 不動点コンビネーター ffffff…f(g(f)) = g(f) 関数 g をいっかい嚙ますことで 本来の関数 f を g(f) で再起表現できる …らしい
let の遅延初期化 Swift 1.1 時代 再帰呼び出し可能なクロージャー
let の遅延初期化 Swift 1.1 時代 再帰呼び出し可能なクロージャー
let の遅延初期化 再帰呼び出し可能なクロージャー ▶ var なら宣言と実装を分ければ OK ▶ ただ var の使用はできるだけ避けたい
let の遅延初期化 再帰呼び出し可能なクロージャー ▶ Swift 1.2 からは let でも可能 ▶ let で宣言と実装を分ければ OK
おしまい。
今日の内容 構造体 ▶ オブジェクトとは呼ばなかった AnyObject ▶ 全てのクラスが暗黙的に準拠 ▶ Objective-C ブリッジ可能な構造体も 値のコピー ▶ 代入時はコピー ▶ 最適化(暗黙コピー、配列型の設計)