寄付窓口はこちら

try! Swift xcodeless - the build system #tryswiftconf Day3-7

Daniel Haight

iOSエンジニアです。そうではないふりをしますが。小さなプロダクトコンサルタント会社を経営しています。タイトなジーンズを着、ゆっくりと旅行の荷造りのエキスパートになっています。

twitter.com

xcodebuildが無いとしたらどうしますか?どのようにプロジェクトをビルドするでしょうか?Xcodeがどうやってプロジェクトをビルドするのか、またswiftcとClangを使ってビルドする方法を説明します。

皆さんこんにちは。

f:id:niwatako:20160304150316j:plain

内容が少しスクリーンと食い違います、すみません。

今日はコードインジェクションの話をします。実行中プログラムの挙動を変える、リビルドしないです。

使い方については話をしません、自分で勉強して下さい。

UIの変更などには素晴らしいです。

f:id:niwatako:20160304150448j:plain

これが変更しようとするもの。変更するStringを変えます。

シンプルなサンプルアプリを用意します。

f:id:niwatako:20160304150505j:plain

f:id:niwatako:20160304150520j:plain

申し訳ないですがObjective-Cランタイム、C言語なども使います、目的のための手段です。

f:id:niwatako:20160304150546j:plain

Objcランタイムをインポートして、new_barで置き換えます。

f:id:niwatako:20160304150610j:plain

もともとのメソッドが3行目。この後詳しく話しますが新しいメソッドを呼んでいきます。リプレースメソッドが一番下に用意してありますね。

f:id:niwatako:20160304150720j:plain f:id:niwatako:20160304150726j:plain

update textを押す

f:id:niwatako:20160304150740j:plain

f:id:niwatako:20160304150757j:plain f:id:niwatako:20160304150812j:plain

変わりました!ご清聴ありがとう。

冗談です。

ほとんど、こういう感じでやるわけですが、Swiftの部分が残っています

図で示すとこういうことをしました

f:id:niwatako:20160304150849j:plain

コンパイルされたもの、これだけは選択肢がありません

f:id:niwatako:20160304150920j:plain

外から何かをしたいわけです、外部からコードをロードし実行する。

f:id:niwatako:20160304150951j:plain f:id:niwatako:20160304150956j:plain

新しいターゲットを作って、バイナリを用意してインタラクションします。

f:id:niwatako:20160304151029j:plain f:id:niwatako:20160304151037j:plain

明示的に名前空間を指定しています。

f:id:niwatako:20160304151107j:plain

フレームワークとひも付けされています。

f:id:niwatako:20160304151129j:plain

右側、ビルドしてバンドルをロードしてimpleする

f:id:niwatako:20160304151151j:plain

フレームワークをバンドルに変える。APIが使いやすい。フォルダ構造が変わる、Plistのその部分が変わる程度です。

f:id:niwatako:20160304151217j:plain

f:id:niwatako:20160304151231j:plain f:id:niwatako:20160304151236j:plain

かなり近づいてきました。

f:id:niwatako:20160304151245j:plain

明示的に変えたいものを変えていますね?でも変えたいものがわかっていないと変えられません。

2つやりたいことがあります。現在の実装と同じような実装、新しいフレームワークの中にある実装、それを置き換えたいわけです。変更されたら、古いメソッドと置き換えるわけです。

f:id:niwatako:20160304151348j:plain

与えられたクラスのすべてのメソッドをこのように得られます。

Objective-Cランタイムのメソッドです。

f:id:niwatako:20160304151414j:plain

2番めの問題はバンドルをロードした時になにがついてきたのか。実装がなければ置き換えられません。何処を見れば実装があるのか分からなくてはいけない。ObjCランタイムAPIを使って

f:id:niwatako:20160304151457j:plain f:id:niwatako:20160304151503j:plain

すべてのクラス、バンドルをロードする。

f:id:niwatako:20160304151519j:plain

このようになりますbarの部分を置き換えたいわけですね。ただ1つ問題があります。新しいクラスをbundleの中に作っていますが、これは名前としてFixされています。

これは簡単に直せますが。。。

f:id:niwatako:20160304151610j:plain

モジュール名をアプリと同じにすれば。今回は飛ばします。

いろんな情報をしかるべき場所に置けばすべてのメソッドを取れるのでInjection出来る

f:id:niwatako:20160304151650j:plain f:id:niwatako:20160304151656j:plain

先ほどと似たようなコードになります。もうちょっと一般的になって、指定せずに差し替えるものを見つけます。

f:id:niwatako:20160304151725j:plain f:id:niwatako:20160304151729j:plain f:id:niwatako:20160304151735j:plain

ダイナミックというキーワードがありましたがいいませんでした。これをダイナミックにする必要はないはずだと考えとってみます。

すると動きません

f:id:niwatako:20160304151805j:plain f:id:niwatako:20160304151808j:plain

ダイナミックとは何でしょう。ObjCのダイナミックディスパッチに参加します。

f:id:niwatako:20160304151831j:plain

SwiftFunctionはObjective-Cのダイナミックディスパッチとは違う動きをする。

fooがメソッドリストを持っていて、セレクタが入っていて、最初のものが選ばれてそれが実行される。

Swiftは?

f:id:niwatako:20160304151912j:plain

公式ドキュメントはないです。でもObjective-Cににていて、いくつか追加があります。

f:id:niwatako:20160304151936j:plain

最初のスーパークラスキャッシュ...Objective-Cクラスはこのようになっています。ExtraSwiftの部分がありポインタがあります。ここでは使いません。

こんな感じです。

f:id:niwatako:20160304152027j:plain

vTableは長さがわかりません、メソッドがいくつ入っているかわからない。

vTableの長さをまず確認する必要がある。sizeがわかってるから簡単なはず。

f:id:niwatako:20160304152103j:plain

ドキュメント化されていないLowレベルのSwiftランタイムをpointerでなんとかします。

f:id:niwatako:20160304152123j:plain

これさえあればいいんです。

こういうことです。

f:id:niwatako:20160304152143j:plain

これがオチです。そんなに大丈夫じゃない。。

何をしているか見ていきましょう。

f:id:niwatako:20160304152205j:plain

InjectiveClassをCStructとしてキャスティングします

f:id:niwatako:20160304152231j:plain

vTableのサイズはstructサイズマイナスその手前、引き算すれば求まります。

そしてメモリをコピーします。

f:id:niwatako:20160304152308j:plain

これをInjectNewクラスに渡すとメソッドもvTableもコピーする。 f:id:niwatako:20160304152326j:plain

f:id:niwatako:20160304152346j:plain

デモをお見せします。

Swiftがあって、通常ならコードインジェクションというとリロードという感じかもしれませんが、変更を加えたらコードの中でレイアウトや制約を変えたら目の前で変えたい、それがPlaygroundの良さ。

実装を変えるにはボタンを変えて変更しましたが、そういったやり取りなしにしたい。

f:id:niwatako:20160304152443j:plain

トリガーが必要、そして新しいメソッドが使えるようになったというコールバックが必要。

一番上は簡単にできるはずです、EventAPIで。構築するバンドルを見ていって。。

コールバックはNSNotificationスキームを使うとか。

ここではこのようにグローバルArrayを追加してオブジェクトに追加して、コールバックとして扱う。

f:id:niwatako:20160304152603j:plain f:id:niwatako:20160304152607j:plain

DEMO

f:id:niwatako:20160304152825j:plain f:id:niwatako:20160304152851j:plain

f:id:niwatako:20160304152950j:plain f:id:niwatako:20160304153005j:plain

今までやったのは全部小さなアイディアです。非常にシンプルなスタンダードメソッドにもとづいています。

一つのメソッドを実装する時に皆さんが知っているものを使いながら置き換えをかけていきました。

順番に高度なものに置き換えている

f:id:niwatako:20160304153049j:plain 

複雑なものは簡単なもので成り立っている。

f:id:niwatako:20160304153102j:plain

皆さんありがとう!

f:id:niwatako:20160304153251j:plain

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

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

そして良いまとめ記事があったらはてなブックマークでブックマークしましょう! try! Swift の記事で盛り上がると嬉しいです!