Daniel Haight
iOSエンジニアです。そうではないふりをしますが。小さなプロダクトコンサルタント会社を経営しています。タイトなジーンズを着、ゆっくりと旅行の荷造りのエキスパートになっています。
xcodebuildが無いとしたらどうしますか?どのようにプロジェクトをビルドするでしょうか?Xcodeがどうやってプロジェクトをビルドするのか、またswiftcとClangを使ってビルドする方法を説明します。
皆さんこんにちは。
内容が少しスクリーンと食い違います、すみません。
今日はコードインジェクションの話をします。実行中プログラムの挙動を変える、リビルドしないです。
使い方については話をしません、自分で勉強して下さい。
UIの変更などには素晴らしいです。
これが変更しようとするもの。変更するStringを変えます。
シンプルなサンプルアプリを用意します。
申し訳ないですがObjective-Cランタイム、C言語なども使います、目的のための手段です。
Objcランタイムをインポートして、new_barで置き換えます。
もともとのメソッドが3行目。この後詳しく話しますが新しいメソッドを呼んでいきます。リプレースメソッドが一番下に用意してありますね。
update textを押す
変わりました!ご清聴ありがとう。
冗談です。
ほとんど、こういう感じでやるわけですが、Swiftの部分が残っています
図で示すとこういうことをしました
コンパイルされたもの、これだけは選択肢がありません
外から何かをしたいわけです、外部からコードをロードし実行する。
新しいターゲットを作って、バイナリを用意してインタラクションします。
明示的に名前空間を指定しています。
フレームワークとひも付けされています。
右側、ビルドしてバンドルをロードしてimpleする
フレームワークをバンドルに変える。APIが使いやすい。フォルダ構造が変わる、Plistのその部分が変わる程度です。
かなり近づいてきました。
明示的に変えたいものを変えていますね?でも変えたいものがわかっていないと変えられません。
2つやりたいことがあります。現在の実装と同じような実装、新しいフレームワークの中にある実装、それを置き換えたいわけです。変更されたら、古いメソッドと置き換えるわけです。
与えられたクラスのすべてのメソッドをこのように得られます。
Objective-Cランタイムのメソッドです。
2番めの問題はバンドルをロードした時になにがついてきたのか。実装がなければ置き換えられません。何処を見れば実装があるのか分からなくてはいけない。ObjCランタイムAPIを使って
すべてのクラス、バンドルをロードする。
このようになりますbarの部分を置き換えたいわけですね。ただ1つ問題があります。新しいクラスをbundleの中に作っていますが、これは名前としてFixされています。
これは簡単に直せますが。。。
モジュール名をアプリと同じにすれば。今回は飛ばします。
いろんな情報をしかるべき場所に置けばすべてのメソッドを取れるのでInjection出来る
先ほどと似たようなコードになります。もうちょっと一般的になって、指定せずに差し替えるものを見つけます。
ダイナミックというキーワードがありましたがいいませんでした。これをダイナミックにする必要はないはずだと考えとってみます。
すると動きません
ダイナミックとは何でしょう。ObjCのダイナミックディスパッチに参加します。
SwiftFunctionはObjective-Cのダイナミックディスパッチとは違う動きをする。
fooがメソッドリストを持っていて、セレクタが入っていて、最初のものが選ばれてそれが実行される。
Swiftは?
公式ドキュメントはないです。でもObjective-Cににていて、いくつか追加があります。
最初のスーパークラスキャッシュ...Objective-Cクラスはこのようになっています。ExtraSwiftの部分がありポインタがあります。ここでは使いません。
こんな感じです。
vTableは長さがわかりません、メソッドがいくつ入っているかわからない。
vTableの長さをまず確認する必要がある。sizeがわかってるから簡単なはず。
ドキュメント化されていないLowレベルのSwiftランタイムをpointerでなんとかします。
これさえあればいいんです。
こういうことです。
これがオチです。そんなに大丈夫じゃない。。
何をしているか見ていきましょう。
InjectiveClassをCStructとしてキャスティングします
vTableのサイズはstructサイズマイナスその手前、引き算すれば求まります。
そしてメモリをコピーします。
これをInjectNewクラスに渡すとメソッドもvTableもコピーする。
デモをお見せします。
Swiftがあって、通常ならコードインジェクションというとリロードという感じかもしれませんが、変更を加えたらコードの中でレイアウトや制約を変えたら目の前で変えたい、それがPlaygroundの良さ。
実装を変えるにはボタンを変えて変更しましたが、そういったやり取りなしにしたい。
トリガーが必要、そして新しいメソッドが使えるようになったというコールバックが必要。
一番上は簡単にできるはずです、EventAPIで。構築するバンドルを見ていって。。
コールバックはNSNotificationスキームを使うとか。
ここではこのようにグローバルArrayを追加してオブジェクトに追加して、コールバックとして扱う。
DEMO
今までやったのは全部小さなアイディアです。非常にシンプルなスタンダードメソッドにもとづいています。
一つのメソッドを実装する時に皆さんが知っているものを使いながら置き換えをかけていきました。
順番に高度なものに置き換えている
複雑なものは簡単なもので成り立っている。
皆さんありがとう!
不可能だと思った黒魔術ことが出来る。めっちゃすごい。おもしろかった #tryswiftconf
— MasaIchi (@masaichi) March 4, 2016
Thanksで一番目に紹介されてる方のGithubは黒魔術が沢山あって面白いです https://t.co/uipwj5pPso #tryswiftconf
— すらいむ (@horimislime) March 4, 2016
Swiftのカンファレンスでvtableの話が出るとは思いませんでしたw
— TSG@try!Swift参加中 (@tsgcpp) March 4, 2016
C++好きなので、とてもおもしろかったです! #tryswiftconf
気に入った記事は はてなブックマーク
はてなブックマークアプリiOS開発チームから来ました! はてなブックマークにはSwift特集があります! 良い記事を見逃さないように、ご利用ください! http://b.hatena.ne.jp/hotentry/it/Swift
そして良いまとめ記事があったらはてなブックマークでブックマークしましょう! try! Swift の記事で盛り上がると嬉しいです!