kateinoigakukunのブログ

思考垂れ流しObserver

19になりました。

こんにちは。

例年のごとく今年もやってきた誕生日ですが、今年は気がつくと謎のアドベントカレンダーにjoinしてました。

誕生日を迎えました(9/16追記)|noppe*|note

誕生日アドベントカレンダー、次回は家庭の医学くんです。(ノンアポ)

ということで当日からしばらく経ってしまいましたが、欲しいものリストからいただいたものリストを公開していこうと思います。

リストには図々しくも、普通に読みたかった技術書たちをドサっと入れました。

@kishikawakatsumiさんから

詳解UNIXプログラミング 第3版

詳解UNIXプログラミング 第3版

どデカいダンボールで届いたので、むむ、これは本では無いな、とエスパーしたのですが、本という名の鈍器が入っていました。

Amazonの商品画像は正面の図しか教えてくれないので、まさかここまでとは思わず…

ドンッ

先人が築いてきた土台の厚みですね。

最近は興味の方向が段々と下に向いてきたので、楽しく読めそうです。

オーデュボンの祈り (新潮文庫)

オーデュボンの祈り (新潮文庫)

もう一つ、伊坂幸太郎の作品をいただきました。友人に勧められて以来この作家さんの作品をいくつか読んでいるのですが、珍しくハマった作家さんです。

ありがとうございます!

@bannzaiさんから

bannzaiさんのnは2つなんですね。

きつねさんでもわかるLLVM ~コンパイラを自作するためのガイドブック~

きつねさんでもわかるLLVM ~コンパイラを自作するためのガイドブック~

わいわいswifrcのイベントページに載っている、もはや検定教科書と言っても過言では無い本です。

じつはまだ読んだことなくて、雰囲気でLLVM触ってるのでありがたいです!

果たして僕はきつねさんになれるのでしょうか。

乞うご期待です。

‪@noppefoxwolf‬さんから

伊藤園 1日分の野菜 (紙パック) 200ml×24本

伊藤園 1日分の野菜 (紙パック) 200ml×24本

野菜ジュースをいただきました!

大学の食堂の混み具合が異常すぎて、入学後2週間で昼のメニューを野菜ジュースとラムネにシフトした僕にとっては非常にありがたいです。

メッセージに

異世界転生の仕方を教えてください

とあったのですが、たまたまそれを見た母親に怪訝な目をされました!今日も我が家は平和です!


9/17 追記

@kishikawakatsumiさんから(その2)

なんと第2弾があったようで、リンカの本をいただきました。

今年の夏の前半でLinkers & Loadersを読んで「リンカ完全に理解した」状態になったのですが、最近また「何も分からん」のフェーズに入ったので最高のタイミングでした!

パラパラめくってみたところ、Linkers & Loadersとはまた毛色が違うようなので、まだまだ学べることがありそうです。ありがとうございます!!

@d_dateさんから

データ構造とアルゴリズム

データ構造とアルゴリズム

大学の図書館で読めそうじゃんこれ というものにしました

というメッセージと共に、まさに図書館にありそうな本をいただきました!

絶対に図書館にあると思いますが、手元にある安心感は何にも代え難いです!

実はアルゴリズムの類の本は全く読んだことが無く、必要に応じてwikipediaで理解していただけなので、脳の引き出しを拡張するために勉強したいと思います。

ありがとうございます!

@takasekさんから

マイクロサービスアーキテクチャ

マイクロサービスアーキテクチャ

これからも無双してください

最近は暇さえあればマイクロサービスのアーキテクチャについて思いを馳せているので、これを読んで無双できるようなアーキテクチャへの道を見出したいと思います!

あと🐝が思ったよりリアルでした!!

ありがとうございます!

@giginetさんから

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

完全にたかりの図ですが、本当にありがたいことにDDD本を頂きました。

正しくドメインを切り出せるようになるのが直近の目標なので、無限に吸収できることが詰まっている予感です。

これでOSSを全部いい感じにしてくれ〜

という雑で壮大なタスクがメッセージとして降ってきたので、全部いい感じにしたいとおもいます。

僕とgiginetさんがメインでメンテナをしてるIBLinterをよろしくお願いします GitHub - IBDecodable/IBLinter: A linter tool for Interface Builder

@crcrpar

森永製菓 大粒ラムネ 41g×10袋

森永製菓 大粒ラムネ 41g×10袋

僕の生活を支える技術の一つである「大粒ラムネ」をデプロイしていただきました! 野菜ジュースとラムネの最強タッグでしばらくお昼ご飯難民にならなくて済みそうです!

ありがとうございます〜!


多分全部読むのに1年くらいかかりそうですが、無限の伸び代を感じることができて非常にワクワクしてます。

やるぞやるぞ〜

みなさんありがとうございました!

この恩はいろんな形で還元していきたいと思います。

これからも仲良くしてください〜〜

Inside SwiftUI (About @State)

SwiftUI was announced at this WWDC and we've tried it for a few days. This WWDC was the moment everything changed for me.

But SwiftUI is still a private source software as well as UIKit and we need to develop applications while speculating the behavior of it.

There is some features which I felt is a mystery in SwiftUI. I investigated them and internal implementation of SwiftUI.

(This post is just a my prediction)

@State

@State var value: Int

You may write this many time while doing tutorials of SwiftUI. Do you understand how State works?

This feature uses Property Delegates introduced from Swift5.1 and it allows to delegate the implementation of getter and setter to another instance (In this case "another instance" means State).

State is a struct defined in SwiftUI using @propertyDelegate. If State is changed, View will be re-rendered.

First, let's implement State by myself!

@propertyDelegate
struct State<Value> {
    var storage: Value
    var value: Value {
        get { storage }
        set {
            storage = newValue
            renderView()
        }
    }
    init(initialValue value: Value) {
        self.storage = value
    }
    
    func renderView() {
        // ???
    }
}

struct ContentView: View {
    @State var text: String
}

It's almost easy to implement but I couldn't figure out how re-render View. SwiftUI seems not re-render the all Views but some particular views whose state is modified. So I need to link ContentView and text: State<String> to update the view.

Internal structure of State

Let's dump the state instance to investigate internal structure of State by using dump.

struct ContentView: View {
    var text = State<String>(initialValue: "Hello")
    
    init() {
        print("Init:")
        dump(text)
    }
    
    var body: some View {
        print("Body:")
        dump(text)
        return Text("Hello, world")
    }
}

I dumped text in init and body but their outputs are different.

Init:
▿ SwiftUI.State<Swift.String>
  - _value: "Hello"
  - _location: nil

Body:
▿ SwiftUI.State<Swift.String>
  - _value: "Hello"
  ▿ _location: Optional(SwiftUI.StoredLocation<Swift.String>)
    ▿ some: SwiftUI.StoredLocation<Swift.String> #0
      - super: SwiftUI.AnyLocation<Swift.String>
        - super: SwiftUI.AnyLocationBase
      ▿ viewGraph: Optional(SwiftUI.ViewGraph)
        ...

This result shows ViewGraph appears after init but before body. It seems there is a way to inject ViewGraph into State outside of View.

Since ViewGraph is an internal type of SwiftUI, I can only speculate it but it seems ViewGraph manages tree structure of View. If this ViewGraph can render View selectively, it's easy to imitate the re-rendering system.

Then, let's fill the body of renderView.

 @propertyDelegate
 struct State<Value> {
     var storage: Value
+    var viewGraph: ViewGraph?
     var value: Value {
         get { storage }
         set {
             storage = newValue
             renderView()
         }
     }
     init(initialValue value: Value) {
         self.storage = value
     }
 
+    func setViewGraph(_ viewGraph: ViewGraph) {
+        self.viewGraph = viewGraph
+    }
 
     func renderView() {
+        viewGraph.render()
     }
 }

It became clear that how re-render view but it's still uncertain that how inject the ViewGraph into ContentView and make the relationship between State and View.

@State filed belongs to View but there is no way to access text: State<String> through View protocol because the field name is not bound by the protocol. For example, it can be easy to inject if State field is bound by the protocol as follows code. But actually, fields can be named freely.

protocol ViewGraphInjectable {
    var state: State<Value> { get }
}

struct ContentView: View, ViewGraphInjectable {
    @State var state: String
}

Reflection API

The only way to get fields without protocol I first thought is Mirror. It's very simple that getting fields of View using Mirror and if there is State, call setViewGraph.

But does SwiftUI really use Mirror? If Mirror is used, CustomReflectable.customMirror should be called and print "Mirror is used!".

struct ContentView: View, CustomReflectable {
    @State var text: String
    var customMirror: Mirror {
        print("Mirror is used!")
        return Mirror(reflecting: self)
    }
}

But customMirror is not called. For checking, I tried to set breakpoint at swift_reflectionMirror_count which is runtime function used by Mirror but it also doesn't break.

This result indicates that SwiftUI get fields without using Mirror.

Reflection without Mirror

There is still a way to get fields without using Mirror. It's using metadata.

If you don't know metadata, you can learn it by my try! Swift presentation.

Metadata has Field Descriptor which contains accessors for fields of the type. It's possible to get fields by using it.

My various experiments result AttributeGraph.framework uses metadata internally. AttributeGraph.framework is a private framework that SwiftUI use internally for constructing ViewGraph.

You can see it by the symbols of the framework.

$ nm /System/Library/PrivateFrameworks/AttributeGraph.framework/AttributeGraph

There is AG::swift::metadata_visitor::visit_field in the list of symbols. i didn't analysis the whole of assembly code but the name implies that AttributeGraph use visitor pattern to parse metadata. If you set break point, it surely hit.

Why not use Mirror

So why is metadata used instead of Mirror? I think it's for performance.

Mirror.children is represented as [(label: String, value: Any)] to hold any type of value. But Any wraps the actual value and when you use it, Any is unwrapped every time. SwiftUI uses View many times and the overheads can be critical problem.

On the other hand, Using raw pointer through Field Descriptor doesn't affect performance seriously.

Flow to update View

  1. Find State of View using Field Descriptor
  2. Inject ViewGraph into State
  3. Render View.body
  4. State is changed
  5. State notify the ViewGraph to update view
  6. Re-render View.body

Only Apple knows the actual implementation. But it's certain that AttributeGraph.framework has its own reflection system.

I said "I'm looking forward to your great libraries using metadata" in try! Swift but I had never thought Apple do it, I think Apple started to use metadata because ABI stability was built since Swift5. In fact, ABI stability brings us great benefits!

I'll write an article about DynamicViewProperty by next week.

Inside SwiftUI @State編

WWDCでSwiftUIが発表されてから数日が経ちました。一気に世界が変わった気がしますね。 ただ、UIKitと同様にSwiftUIはオープンソースでは無いため、我々開発者は依然挙動をエスパーしながら開発する必要があります。

その中でも、SwiftUIのチュートリアルを試している中で不思議な仕組みがいくつかあったので、僕が調べたSwiftUIの内部構造について書き留めておきます。

(あくまで考察なので間違っていても悪しからず)

追記(2022-09-15)

未だにアクセスがあるので追記。

TokamakというSwiftWasmでSwiftUI互換なコードを書くためのUIフレームワークがあり、 ここで考察した内容とほぼ同じようなテクニックを使ってStateを実装しています。 具体的な実装が気になる方は読んでみてください。

github.com

目次

  • @State編(今ココ)
  • DynamicViewProperty編(来週くらいには書きます)
  • 差分更新編(調査中)

@State

@State var value: Int

チュートリアルをこなした方であれば何度も書いたことでしょう。

Swift5.1からのProperty Delegatesを使った記法で、プロパティの値のgetterとsetterを別の型(ここではState)に委譲できます。

StateというのはSwiftUIに定義された@propertyDelegateなstructです。変更されるとStateが定義されているViewが再レンダリングされる、という振る舞いをします。

ではこの振る舞いからStateの実装を予想してみます。

@propertyDelegate
struct State<Value> {
    var storage: Value
    var value: Value {
        get { storage }
        set {
            storage = newValue
            renderView()
        }
    }
    init(initialValue value: Value) {
        self.storage = value
    }
    
    func renderView() {
        // ???
    }
}

struct ContentView: View {
    @State var text: String
}

(本当はvalueのsetterがnonmutatingなのでbox化されてるはずですが説明のため省略してます。)

簡単に実装できましたがViewを再レンダリングする部分だけは想像できません。keyWindow配下のViewを全て再レンダリングしているのか?、と一瞬思いましたが、Stateが変更されてもルートのViewから全て再レンダリングされる訳では無さそうです。 では、どうやってtext: State<String>ContentViewを紐付けてContentViewだけ再レンダリングしているのでしょうか。

Stateの内部構造

とりあえずStateのpublicなフィールドにはそれっぽい物は無いので、内部フィールドをdumpで調べてみます。

struct ContentView: View {
    var text = State<String>(initialValue: "Hello")
    
    init() {
        print("Init:")
        dump(text)
    }
    
    var body: some View {
        print("Body:")
        dump(text)
        return Text("Hello, world")
    }
}

(StatedelegateValueを使っており$textStateを返さないのでProperty Delegateを使わない記法にあえてしています。)

initbodyでそれぞれtext: State<String>をdumpしましたが、実は違った結果が出力されます。

Init:
▿ SwiftUI.State<Swift.String>
  - _value: "Hello"
  - _location: nil

Body:
▿ SwiftUI.State<Swift.String>
  - _value: "Hello"
  ▿ _location: Optional(SwiftUI.StoredLocation<Swift.String>)
    ▿ some: SwiftUI.StoredLocation<Swift.String> #0
      - super: SwiftUI.AnyLocation<Swift.String>
        - super: SwiftUI.AnyLocationBase
      ▿ viewGraph: Optional(SwiftUI.ViewGraph)
        ...

ViewGraphという怪しいオブジェクトが見えるようになりました。initとbodyの間に何かしらの方法でStateにViewGraphが注入されています。

Internalな型なので名前から推測するしかありませんが、どうやらViewGraphはViewのツリー構造を管理するオブジェクトのようです。このViewGraphがContentViewのみを選択的にレンダリングできる、と仮定すると再レンダリングの仕組みは説明できそうです。 では、StateがViewGraphを持つことを考慮して最初のStateの実装の???を埋めてみます。

 @propertyDelegate
 struct State<Value> {
     var storage: Value
+    var viewGraph: ViewGraph?
     var value: Value {
         get { storage }
         set {
             storage = newValue
             renderView()
         }
     }
     init(initialValue value: Value) {
         self.storage = value
     }
 
+    func setViewGraph(_ viewGraph: ViewGraph) {
+        self.viewGraph = viewGraph
+    }
 
     func renderView() {
+        viewGraph.render()
     }
 }

なるほど、Viewを再レンダリングする方法は分かりました。しかし、未だどうやってContentViewStateを紐付けてContentViewViewGraphを注入するのか、は謎のままです。

先ほどの実験からinitbodyの間に呼ばれることは分かっていますが、どこから呼ばれるかは不明です。

また、@StateフィールドはViewに対して生えていますが、protocolでフィールド名が縛られている訳では無いのでtext: State<String>にアクセスする方法がありません。 例えば以下のようにprotocolで縛られていれば注入できると思いますが、実際フィールド名は自由に付けられるので他の方法で実現されているようです。

protocol ViewGraphInjectable {
    var state: State<Value> { get }
}

struct ContentView: View, ViewGraphInjectable {
    @State var state: String
}

Reflection API

protocolを使わずに任意の構造体から動的に値を取り出す、となると真っ先に思いつくのがMirrorです。ViewMirrorにかけて、childrenを取り出して、その中にState型があればsetViewGraphを呼ぶ、という比較的単純な方法です。

それでは本当にMirrorを使っているか実験してみましょう。CustomReflectableが実装されている場合、MirrorにかけるとcustomMirrorが呼ばれるはずなので、"Mirror is used!"が出力されるはずです。

struct ContentView: View, CustomReflectable {
    @State var text: String
    var customMirror: Mirror {
        print("Mirror is used!")
        return Mirror(reflecting: self)
    }
}

しかし、実際はcustomMirrorは呼ばれません。念のため、Mirrorが使っているランタイムAPIswift_reflectionMirror_countブレークポイントを貼ってみましたがヒットしませんでした。

つまり、SwiftUIは内部でMirrorを使わずにViewのフィールドを取得している訳です。

Mirrorを使わないReflection

Mirrorを使わずともフィールドを取得する方法はまだあります。そうですメタデータを利用する方法です。メタデータについては僕のtry! Swiftの発表を見てもらえると雰囲気が掴めると思います。

メタデータにはField Descriptorという、その型のフィールドのアクセサ群が格納されています。それを利用すればフィールドの一覧を取得できるのでMirrorの代わりに使っている可能性は十分にあります。

色々と試行錯誤した結果1、SwiftUIが内部でViewGraphを組み立てるのに使っているAttributeGraph.frameworkというプライベートフレームワークが内部でメタデータを使っている事が分かりました。

では早速AttributeGraph.frameworkのシンボルをnmコマンドで見てみましょう。

$ nm /System/Library/PrivateFrameworks/AttributeGraph.framework/AttributeGraph

AG::swift::metadata_visitor::visit_field というシンボルが見えると思います。アセンブリを全て読んだ訳ではありませんが、名前から察するにメタデータからフィールドをVisitorパターンで回していそうです。ブレークポイントを貼ってみると実際に動いている様子が確認できます。

なぜMirrorを使わないのか

ではなぜMirrorでもできることをメタデータで実装しているのでしょうか。完全に私の考察ですが、パフォーマンスが主な理由だと思っています。

Mirrorchildren[(label: String, value: Any)]で表現します。任意の型を表現するのにAnyを使っていますが、Anyは値をラップしているため、実際の値を使うには毎回取り出す必要があります。Viewが多くなればなるほどこのオーバーヘッドは無視できなくなってくるため、フィールドをポインタで直接操作できるField Descriptorを使っている、というのが予想です。

StateがViewに更新されるまで

ここまでの実験でStateが変更された際にどうやってViewが再レンダリングされるか予想できたのでまとめます。

  1. Viewに対してField DescriptorStateを探してViewGraphを注入する
  2. View.bodyを評価してレンダリングする。
  3. Stateが変更される
  4. StateViewGraphに変更を通知する
  5. View.bodyが再評価されViewが更新される。

実際の実装はAppleのみぞ知るところですが、これ以上のアイディアは思いつきませんでした。ただ、AttributeGraph.frameworkが独自にリフレクションの仕組みを使っているのは確実です。

try! Swiftの発表で「メタデータを使ったライブラリをみんな作ってみてね」と言いましたが、まさかAppleがABI安定化を期に率先して使ってくるとは思いませんでした。今年のWWDCは驚くことばかりですね。

次はDynamicViewPropertyについて書きます。


  1. これは先に調査していたDynamicViewPropertyの仕組みを考えた上でのエスパーです。

try! Swift 2019

毎年期末試験の時期と重なって参加を断念していたので初参加でした。

参加できるだけでも嬉しかったのですが、光栄なことにスピーカーとして登壇することもできました。

大きなカンファレンスでの登壇は昨年のiOSDCが最初で今回はそれ以来です。ただ、今回は規模がさらに大きいことや、英語での登壇だったこともあり余裕は全くなかったです。

どのくらい余裕がなかったかというと、2日目、登壇する直前の午前中のトークをほぼ覚えていないくらいです。絶対にキャッチアップするので許してください。

登壇

Swift Type Metadata (en) - kateinoigakukunのブログ

Swift Type Metadata (ja) - kateinoigakukunのブログ

Swiftのメタデータについて話しました。内容的には年末に書いたアドカレの発展版です。 Swift Type metadata - Qiita

わいわいswiftcで@omochimetaruが楽しそうに話していたのがきっかけで調べ始めたのですが、ここら辺の技術は掘れば掘るほど面白かったです。

ただ、発表は20分しか無い上に900人にウケる内容にする必要があるので、風呂敷を広げすぎないように構成するのが大変でした。シュッと飛ばした部分が結構あるので、今回の発表で興味を持った方はこの辺を見てもらえると色々と繋がってきて楽しいと思います。

後半で紹介した拙作のOSSはメルカリのインターンの中で作ったものです。基本的なアイディアはCodableのデコードの仕組みに乗っかって任意の構造体を組み立てる、という簡単なものです。

最初はenumをサポートしておらず、各自書いてもらう方式をとっていたのですが、今回のスピーカーで僕のメンターでもある@tarunonから、どうにかしてくれ〜と御達しがあったので、なんとか作りました。

初めてコンパイラの勉強が業務の役に立ちました。

英語で話したこともあってか、Ask the speakerの時間に何人か海外の方もいらしてくださり、今回は言及出来なかったstaticなメタプログラミングとの比較や具体的なユースケースの実装相談など、楽しく議論することができました。何より自分の話した内容が伝わっていることが実感できて嬉しかったです。

セッション

個人的に面白かったトークは 「protocol/extensionにジェネリクスを入れたい」です。型パラを静的に分解して詰め直す技がテクニカルでした。カンファレンス中にお話する機会が何度かあったのですが、Swiftに限らない言語の型システムの深い理解から来る興味深い話が聞けました。

ワークショップ

ワークショップはOpen Source Swiftに参加しました。以前自分が報告した SR-7467 に取り組みました。調査を始めた時はTypeCheckerのキャッシュ機構のバグだと思っていたのですが実際の問題は別のところにあり、相当時間を使ってしまいました。最終的にバグの原因は完全にわかったのですが、色々と考慮漏れがあり未だテストが通ってません。

[SR-7467] Skip validation while checking protocol conformance by kateinoigakukun · Pull Request #23514 · apple/swift · GitHub

今後

やりたいこと、勉強したいことは増えるばかりで時間がいくらあっても足りません。正しく取捨選択していくぞ

Swift Type Metadata (en)

www.youtube.com

try! Swift 2019

I'm kateinoigakukun, working at Mercari as an intern. Today, I'll talk about Metadata which is one of the most important things to understand how Swift works.

Swift is well-known for being a statically-typed language, but it actually has many dynamic things on runtime.

let typeName = String(describing: Int.self)

I'm sure all of you have looked at StackOverflow and written code like this to get a type name.

extension UITableView {
    func register<Cell>(nibWithCellClass: Cell.Type) where Cell: UITableViewCell {
        let typeName = String(describing: Cell.self)
        let nib = UINib(nibName: typeName, bundle: Bundle.main)
        register(nib, forCellReuseIdentifier: typeName)
    }
}

tableView.register(nibWithCellClass: TweetCell.self)

For example, when you call register method for UITableViewCell, you use this to match the xib name and type name. It's useful extension. But have you ever thought about how this code works on runtime? This is your first step towards thinking about memory representation in Swift. Let's dig into the world of metadata!

Agenda

  1. What is type metadata?
  2. Explore String(describing: Int.self)
  3. How to use metadata in Swift
  4. Use cases in OSS

First, I'll explain "What is type metadata". Swift type metadata is not something we are familiar with. But we usually get the benefits of it, because Core features of Swift use it for dynamic behavior. I'll explain the example of the String initializer and talk about how type metadata is used inside of Swift. Then I'll introduce how to use metadata in Swift and some examples of hacking Swift.

What is type metadata?

  • Type information in Swift runtime
  • Used in Swift internal dynamic behavior
  • Metatype is pointer to metadata
let metatype: Int.Type = Int.self

Let's start. Type metadata is Swift's internal information about types like instance size, number of cases of enum, and so on. This information is stored statically in binary or generated dynamically in runtime. Metatype is represented as type name plus self keyword and the actual value is a pointer to the metadata.

extension String {

  public init<Subject: CustomStringConvertible>(describing instance: Subject) { ... }

  public init<Subject>(describing instance: Subject) { ... }

}

let typeName = String(describing: Int.self) // "Int"

Int.self is a metatype object and it is passed to the String initializer. This initializer accepts any type of value, and returns the description property if the type of the value conforms to CustomStringConvertible and returns the type name if a metatype is passed in.

extension Int.Type: CustomStringConvertible { // 🚫 Cannot extend a metatype 'Int.Type'
    var description: String {
        return "Int"
    }
}

It can not be true that metatype implements the description property of CustomStringConvertible because there is no way to extend metatype. And it seems impossible to implement with the pure Swift API because Swift doesn’t have a runtime API like Objective-C. So there should be some magic.

SwiftCore

  • Swift standard library
  • Fundamental types and interfaces

SwiftRuntime

  • Swift runtime library
  • Dynamic behavior

The initializer is implemented in SwiftCore and the magic is implemented in SwiftRuntime. SwiftCore is Swift standard library written in Swift and contains fundamental data types like String and Int and protocols. SwiftRuntime is the Swift runtime library written in C++ and contains runtime behavior. Many dynamic features like dynamic casting and allocating instances are implemented in this library.

stdlib/public/core/Mirror.swift

struct String {
  public init<Subject>(describing instance: Subject) {
    _print_unlocked(instance, &self)
  }
}

Swift is open source, so we can see this on Github. In this initializer, the print_unlocked which is internal function is called.

stdlib/public/core/Misc.swift

public func _typeName(_ type: Any.Type, qualified: Bool = true) -> String {
  let (stringPtr, count) = _getTypeName(type, qualified: qualified)
  return String._fromUTF8Repairing(
    UnsafeBufferPointer(start: stringPtr, count: count)).0
}

@_silgen_name("swift_getTypeName")
public func _getTypeName(_ type: Any.Type, qualified: Bool) -> (UnsafePointer<UInt8>, Int)

Digging into the call stack for the case when type of argument is metatype, the _typeName function is called, and calls another function _getTypeName. Look at the _getTypeName definition. First, you can see that the function has @_silgen_name attribute and doesn't have body code. What’s this? This @_silgen_name attribute specifies the function name that the declaration will have at link time. In this use case, this definition is used to link to a function in SwiftRuntime. I'll skip the details of the linked function, but it simply extracts the type name from the metadata. Then, how is metadata represented in the memory world?

As you can see in the figure, metadata is broken down and represented as - Value witness table which is a group of functions for manipulating instance

  • a kind value which represents the kind of type such as class, struct, protocol, etc,
  • and NominalTypeDescriptor which records detailed information of the type.
  • In the case of a class, VTable is also included,
  • and in the case of a generic type, type parameters are embedded dynamically.

So, in the NominalTypeDescriptor there is the type name we are looking for. We can get the type name from the nominal type descriptor by just advancing the metadata pointer. It seems not difficult to implement, so let's reproduce the String initializer in SwiftRuntime. (docs/ABI/TypeMetadata.rst)

struct StructMetadata {
    let kind: Int
    let typeDescriptor: UnsafePointer<StructTypeDescriptor>
}

struct StructTypeDescriptor {
    let flags: Int32
    let parent: Int32
    let name: RelativePointer<CChar>
}

In the first step, reproduce the memory layout as a struct. Most information about the memory layout is documented but a part of it is already outdated, so we need to read the source code of swift compiler sometimes. To simplify this example, I will only implement for structs. Then, we need to understand RelativePointer to reproduce the memory layout.

(include/swift/Basic/RelativePointer.h)

RelativePointer is not just a pointer. A basic absolute pointer has the address to the referent, but relative pointer has the offset from its own address to the referent's address. It works by just reading the offset and advancing from its own address. Using RelativePointer instead of an absolute pointer reduces relocation.

func getTypeName<Subject>(of type: Subject.Type) -> String {
    let metadataPointer = unsafeBitCast(
        type, to: UnsafePointer<StructTypeMetadata>.self
    )
    let namePointer: UnsafePointer<CChar> = metadataPointer.pointee
                        .typeDescriptor.pointee
                        .name.advancedPointer()
    return String(cString: namePointer)
}

Then, we finished preparing, so let's extract the type name. First, cast the metatype object into the pointer of metadata. But in Swift's type system, metatype doesn't have subtyping relation with metadata pointer, so use unsafeBitCast to cast the metatype. Access the name pointer through the type descriptor and advance the offset to be an absolute CChar pointer. Next convert this to a Swift String. Then, the implementation has been completed!

let typeName = getTypeName(of: Int.self) // "Int"

Execute this, You can get the type name. This is the first step of meta programming with metadata!

Use cases inside of Swift

  • Allocate instance
  • Dynamic method dispatch
    • VTable
  • Reflection

Metadata is used for dynamic behavior in Swift. Many people use Swift without realizing this, but there are many use cases. Where? The most common use case is allocating an instance. And if you call a method through protocol or class, the method table stored in metadata is referenced to get the method reference. In other cases, Mirror API uses metadata to reflect properties.

In this way, metadata is very useful inside of Swift, but we can abuse it.

Method swizzling

Next, I'll talk about the Black magic that you used in Objective-C. The black magic is Method swizzling. If you understand the metadata, you can get great power.

Method swizzling

class Animal {
    func bar() { print("bar") }
    func foo() { print("foo") }
}

struct ClassMetadata {
    ...
    // VTable
    var barRef: FunctionRef
    var fooRef: FunctionRef
}

First, reproduce the memory layout as getting the type name. Class methods are called via VTable that is a table of pointers to functions. So it should work if we replace the pointers.

let metadata = unsafeBitCast(
    Animal.self, to: UnsafeMutablePointer<ClassMetadata>.self
)

let bar = withUnsafeMutablePointer(to: &metadata.pointee.barRef) { $0 }
let foo = withUnsafeMutablePointer(to: &metadata.pointee.fooRef) { $0 }

bar.pointee = foo.pointee

let animal = Animal()
animal.bar() // foo

Get the metadata from metatype using unsafeBitCast and get the both pointers of the functions to swizzle. Then, it's easy to replace them. This is very simple but this works well. Like this, using metadata we can achieve what seems impossible.

Use cases

  • Zewo/Reflection
  • wickwirew/Runtime
  • alibaba/HandyJSON
  • kateinoigakukun/StubKit

Now I will introduce some use cases of metadata in OSS I found. The top two provide a Swifty interface to access metadata information. They are very useful when using metadta. The third one is a JSON serialization library which enables encoding and decoding JSON without mapping configuration.

alibaba/HandyJSON

struct Item: HandyJSON {
    var name: String = ""
    var price: Double?
    var description: String?
}

if let item = Item.deserialize(from: jsonString) {
    // ...
}

This feature has been achieved by Codable with compiler code generation since Swift4, but HandyJSON was created before Codable and uses metadata to make a relationship between value and property name without Objective-C API.

Use cases

  • Zewo/Reflection
  • wickwirew/Runtime
  • alibaba/HandyJSON
  • kateinoigakukun/StubKit

The last use case is my library StubKit.

kateinoigakukun/StubKit

import StubKit

struct User: Codable {
    let name: String
    let age: UInt
}

let user = try Stub.make(User.self)
// User(name: "This is stub string", age: 12345)

This library enables instantiating stubs without any arguments and makes it easy to instantiate struct with many many fields. Most of this feature is implemented with Codable but some features are implemented using type metadata.

kateinoigakukun/StubKit

Before I introduce the use case of metadata, I'll share with you how this stub function works. First, a basic struct forms a tree structure and you can traverse it using Decoder protocol.

So if we prepare the stub of a leaf and inject it while traversing, we can instantiate any type of stub without arguments.

func leafStub<T>(of type: T.Type) -> T {
    guard let stubbable = type as? Stubbable else { return nil }
    return type.stub
}

extension Int: Stubbable {
    var stub: Int { return 12345 }
}

extension enum: Stubbable { // 🚫 Can't extend
    var stub: Self {
        return enumStub()
    }
}

For example, String, Int and enum can be leaf types. It's easy to prepare a stub of basic data types, but enum can be defined by users so we need to prepare all stubs of custom enums manually. I know it's very hard so I implemented generating enum instances using metadata.

func enumStub<T>(of type: T.Type) -> T? {
    if isEnum(type: type) {
        let rawValue = 0
        let rawPointer = withUnsafePointer(to: rawValue) { UnsafeRawPointer($0) }
        return rawPointer.assumingMemoryBound(to: T.self).pointee
    }
    return nil
}

func isEnum<T>(type: T.Type) -> Bool {
    let metadata = unsafeBitCast(type, to: UnsafePointer<EnumMetadata>.self).pointee
    return metadata.kind == 1 // kind value of enum is 1
}

We know that we can cast Int value to enum because they have same memory layout. However the method is only available for enum and it's necessary to check whether the type is enum. Metadata is effective here. The head address of the metadata is which kind the type is class, struct or enum. The kind value of enum is statically 1. So we can check if the type is enum by comparing this value.

Caution

  • ABI stability
  • Responsibility

Swift4.2 has no ABI stability so metadata layout will be broken in Swift5. But we have good news that ABI stability is accomplished in Swift5! So you can make a library easily. But if you want to support both Swift4 and 5, you will need a lot of effort to maintain it. We got great power but please remember that using metadata is not an official way of doing things in Swift. If you use the metadata and particularly if you rewrite it, you must have a deep understanding of metadata. For example, method swizzling may be reverted to the original implementation by optimization. It's necessary to dispatch method through method table to swizzle implementation, but dynamic dispatch can be optimized to static dispatch by "Devirtualize" optimization. You must handle cases like this to use this magic. "With great power comes great responsibility"

Summary

  • Swift uses metadata for dynamic behavior
  • We can use metadata in Swift
  • Let's write meta programming libraries!

Let me wrap up the key points. - First, Swift uses metadata for dynamic method dispatch, reflection API and so on. - Second, we can use it in Swift by reproducing memory layout. It brings big benefits and it's just fun. So, I'm looking forward to your great libraries using metadata. That’s all, thank you all very much.

Swift Type Metadata (ja)

www.youtube.com

try! Swift 2019

kateinoigakukunです。メルカリでインターンをしています。

今日はSwiftを理解するために重要なメタデータについて話します。Swiftは静的型付けな言語として知られていますが、 実はランタイムにおいては動的な部分が多々あるのです。

let typeName = String(describing: Int.self)

皆さんもStackOverflowを見て、一度はこんなコードを書いて型名を取得したことがあるでしょう。

extension UITableView {
    func register<Cell>(nibWithCellClass: Cell.Type) where Cell: UITableViewCell {
        let typeName = String(describing: Cell.self)
        let nib = UINib(nibName: typeName, bundle: Bundle.main)
        register(nib, forCellReuseIdentifier: typeName)
    }
}

tableView.register(nibWithCellClass: TweetCell.self)

例えばUITableViewCellをregisterするとき、Xibのファイル名をクラス名と合わせるために使っていると思います。よくあるextensionですね。一方で、この一行がランタイムでどのように動いているか考えたことはありますか?これを機にSwiftのランタイムのメモリ表現を考えてみませんか? メタデータの世界に飛び込んでみましょう!

Agenda

  1. What is type metadata?
  2. Explore String(describing: Int.self)
  3. How to use metadata in Swift
  4. Use cases in OSS

まず、そもそもメタデータとはなにか、について話します。メタデータは我々がアプリを開発する上であまり馴染みが無いかもしれません。しかし、アプリを作っている時でさえ、Swiftのコア機能は動的な動作のために内部的にメタデータを使っており、実はいつも恩恵を受けているのです。前のスライドで出した例を紐解きながら、Swiftが内部でどのようにメタデータを使っているかを解説します。そして、Swiftでどのようにメタデータを使うか、それを使ったハックを紹介します。

What is type metadata?

  • Type information in Swift runtime
  • Used in Swift internal dynamic behavior
  • Metatype is pointer to metadata
let metatype: Int.Type = Int.self

メタデータは型についてのSwiftの内部情報です。例えばインスタンスのサイズや、enumのcase数などが含まれています。これらの情報はバイナリ上に静的に格納されていたり、ランタイムで動的に生成される場合もあります。Swift上では型名にselfを付けて表現されるメタタイプがメタデータへのポインタとなっています。

extension String {

  public init<Subject: CustomStringConvertible>(describing instance: Subject) { ... }

  public init<Subject>(describing instance: Subject) { ... }

}

let typeName = String(describing: Int.self) // "Int"

前述したように、 Int.selfはメタタイプのオブジェクトであり、そのメタタイプはStringのイニシャライザに渡されます。 このイニシャライザは任意の型の値を受け付けます。渡されてきた値の型が CustomStringConvertibleに準拠している場合はdescriptionプロパティをそのまま返しますが、メタタイプが渡された場合は型名を返す、という挙動になっています。

extension Int.Type: CustomStringConvertible { // 🚫 Cannot extend a metatype 'Int.Type'
    var description: String {
        return "Int"
    }
}

Swiftではメタタイプは拡張できないので、CustomStringConvertibledescriptionプロパティが実装されているはずがありません。そのため、純粋なSwiftのAPIで実装するのは不可能だと思われます。SwiftにはObjective-CのようなランタイムAPIが無いからです。つまり、どこかに魔法があるはずです。

SwiftCore

  • Swift standard library
  • Fundamental types and interfaces

SwiftRuntime

  • Swift runtime library
  • Dynamic behavior

このイニシャライザはSwiftCoreで実装されており、魔法はSwiftRuntimeに実装されています。SwiftCoreには、Swiftで使われるStringIntなどの基本的なデータ型とSequenceNumericなどのプロトコルが実装されています。 また、SwiftRuntimeはSwiftのC++で書かれたランタイムライブラリでランタイムの振る舞いが実装されています。ダイナミックなキャストやインスタンスアロケーションのような多くの動的な言語機能はこのライブラリに実装されています。

stdlib/public/core/Mirror.swift

struct String {
  public init<Subject>(describing instance: Subject) {
    _print_unlocked(instance, &self)
  }
}

Swiftの実装はオープンソースになっているので、Githubからいつでも見ることが出来ます。このイニシャライザの内部ではprint_unlocked関数が呼ばれます。

stdlib/public/core/Misc.swift

public func _typeName(_ type: Any.Type, qualified: Bool = true) -> String {
  let (stringPtr, count) = _getTypeName(type, qualified: qualified)
  return String._fromUTF8Repairing(
    UnsafeBufferPointer(start: stringPtr, count: count)).0
}

@_silgen_name("swift_getTypeName")
public func _getTypeName(_ type: Any.Type, qualified: Bool) -> (UnsafePointer<UInt8>, Int)

引数の型がメタ型である場合の呼び出しスタックを掘り下げていくと、typeName関数がメタ型を引数として呼び出されています。そして、また別の関数_getTypeNameがさらに呼ばれます。 _getTypeNameの定義を見てみましょう。 まず、 @ _silgen_name属性が追加されていること、関数の実装がないことに気がつくと思います。これは何でしょう?この@_silgen_nameアトリビュートは、リンク時に付けられる関数の名前を指定します。 今回の場合、このシグネチャーは SwiftRuntimeの関数にリンクするために使われます。リンクされた関数の実装については省略しますが、単純にメタデータに含まれている型名を取り出すだけです。 では、メタデータはメモリ上でどのように表現されるでしょうか?

図のようにメタデータは分割されて表現されます。 - インスタンスを操作するための関数の集合であるValueWitnessTable - クラス、構造体、プロトコルなどの種類の種類を表すkind value - 型の詳細な情報を保持するNominalTypeDescriptor。 - クラスの場合はVTableもメタデータに含まれています、 - さらに、genericな型の場合、型パラメータも動的に埋め込まれます。

NominalTypeDescriptorには、我々が今求めている型の名前が入っています。 つまり、メタデータのアドレスを進めるだけで、NominalTypeDescriptorから型の名前を取得できるということです。 実装するのは難しく無さそうです。Swiftのコードでこの SwiftRuntimeのStringのイニシャライザの実装を再現してみましょう!

(docs/ABI/TypeMetadata.rst)

struct StructMetadata {
    let kind: Int
    let typeDescriptor: UnsafePointer<StructTypeDescriptor>
}

struct StructTypeDescriptor {
    let flags: Int32
    let parent: Int32
    let name: RelativePointer<CChar>
}

まず最初にランタイムのメモリレイアウトをSwiftのstructとして再現します。メモリレイアウトに関する情報はほとんどドキュメントに書かれていますが、一部は古くなっているため、時々Swiftコンパイラーのソースコードを直接読まなくてはいけません。簡単のため、今回はstructについてのみ実装します。 ここでRelativePointerというポインタの型が登場します。

(include/swift/Basic/RelativePointer.h)

RelativePointerはただのポインターではありません。通常のポインターが参照先へのアドレスを持っているのに対し、RelativePointerは自身のアドレスから参照先のアドレスへのオフセットを保持しています。参照するときはオフセット分アドレスを進めるだけです。absolute addressの代わりにrelative addressを使うことで無駄なアドレスの再配置を減らせます。

func getTypeName<Subject>(of type: Subject.Type) -> String {
    let metadataPointer = unsafeBitCast(
        type, to: UnsafePointer<StructTypeMetadata>.self
    )
    let namePointer: UnsafePointer<CChar> = metadataPointer.pointee
                        .typeDescriptor.pointee
                        .name.advancedPointer()
    return String(cString: namePointer)
}

下準備が出来たので実際に型名を取り出してみましょう。まず、引数から渡ってきたメタタイプをメタデータのポインタにキャストします。ここではSwift上のサブタイピング関係が無いためunsafeBitCastを使います。取り出したメタデータからtype descriptorを経由してnameのrelative pointerにアクセスし、relative pointerからabsolute pointerに変換するとCCharポインターになります。これをSwiftのString型に変換してreturnします。 これで実装は完了しました。

let typeName = getTypeName(of: Int.self) // "Int"

これを動かすと、無事に型名を取得できます。メタプログラミングの第一歩目ですね!

Use cases inside of Swift

  • Allocate instance
  • Dynamic method dispatch
    • VTable
  • Reflection

メタデータはSwiftの動的な振る舞いのためによく使われます。 皆さんはメタデータを使っていることを意識せずにSwiftを書いていますが、Swiftには多くのユースケースがあります。 それはどこでしょう? 最も一般的なユースケースは、インスタンスをアロケートすることです。さらに、protocolclassを介してメソッドを呼び出すと、メタデータに格納されている関数テーブルが参照され、呼び出したいメソッドの参照が取得されます。 他の例としては、プロパティのリフレクションのためにメタデータを使っているMirrorAPIが挙げれられます。

このように便利に使われているメタデータですが、人間はずる賢いので他の使い道を思いついてしまいます。

Method swizzling

ここからは皆さんがObjective-Cのころ使っていたあの黒魔術をSwiftで再現してみましょう。そうです、Method swizzlingです。メタデータを理解することで、大いなる力を手に入れることができるのです。

class Animal {
    func bar() { print("bar") }
    func foo() { print("foo") }
}

struct ClassMetadata {
    ...
    // VTable
    var barRef: FunctionRef
    var fooRef: FunctionRef
}

型名を取得したときと同じ要領でメタデータのメモリレイアウトをstructで再現します。ClassのメソッドはVTableという関数ポインタのテーブルを通して呼び出されるため、そのポインタを書き換えれば実現できそうです。

let metadata = unsafeBitCast(
    Animal.self, to: UnsafeMutablePointer<ClassMetadata>.self
)

let bar = withUnsafeMutablePointer(to: &metadata.pointee.barRef) { $0 }
let foo = withUnsafeMutablePointer(to: &metadata.pointee.fooRef) { $0 }

bar.pointee = foo.pointee

let animal = Animal()
animal.bar() // foo

メタデータのポインタをメタタイプから取り出して、そこからswizzleする関数のポインタをmutableなポインタとして取り出します。すると関数ポインタの値を入れ替えるのは簡単です。このコードは非常にシンプルですがきちんと動きます。 このように、メタデータの情報によって我々は不可能なように見えるものでも実現できるのです。

Use cases

  • Zewo/Reflection
  • wickwirew/Runtime
  • alibaba/HandyJSON
  • kateinoigakukun/StubKit

私が見つけたメタデータを使っているOSSを紹介します。上の2つはメタデータの情報にアクセスするためのSwiftyなインターフェースを提供してくれるライブラリです。3つ目は、マッピングのための明示的な設定なしでJSONエンコードとデコードができるJSONライブラリです。

alibaba/HandyJSON

struct Item: HandyJSON {
    var name: String = ""
    var price: Double?
    var description: String?
}

if let item = Item.deserialize(from: jsonString) {
    // ...
}

この機能はSwift4.0以降ではCodableコンパイラがコード生成を使って達成できますが、HandyJSONCodableが登場する前から動いており、Objective-CAPIなしで動的にプロパティ名とプロパティの値の紐づけのためにメタデータを使っています。

Use cases

  • Zewo/Reflection
  • wickwirew/Runtime
  • alibaba/HandyJSON
  • kateinoigakukun/StubKit

最後のユースケースは私のライブラリStubKitです。

kateinoigakukun/StubKit

import StubKit

struct User: Codable {
    let name: String
    let age: UInt
}

let user = try Stub.make(User.self)
// User(name: "This is stub string", age: 12345)

このライブラリを使うと、たくさんのフィールドを持つstructでも引数なしで簡単にスタブをインスタンス化できます。この機能の大部分は Codableで実装されていますが、いくつかの機能はmetadataを使って実装されています。

メタデータを使った事例を紹介する前にこのスタブをインスタンスする機能がどのように動いているのかを解説します。まず、基本的に我々の使っている構造体は木構造で成り立っており、Decoderプロトコルを使ってトラバースすることができます。

そのため、この木構造中の葉の部分のスタブを準備してトラバースしながらスタブを注入すれば、引数なしで任意のスタブをインスタンス化できます。

func leafStub<T>(of type: T.Type) -> T {
    guard let stubbable = type as? Stubbable else { return nil }
    return type.stub
}

extension Int: Stubbable {
    var stub: Int { return 12345 }
}

extension enum: Stubbable { // 🚫 Can't extend
    var stub: Self {
        return enumStub()
    }
}

例えば、 StringIntURLおよびenumは葉の型になる可能性があります。 基本データの型のスタブを準備するのは簡単ですが、enumはユーザーが定義できるため、自前で定義した全てのenumのスタブを手動で準備する必要があります。これはできれば避けたいですね。そのため、メタデータを使ってenumインスタンスを動的に生成してみました。

func enumStub<T>(of type: T.Type) -> T? {
    if isEnum(type: type) {
        let rawValue = 0
        let rawPointer = withUnsafePointer(to: rawValue) { UnsafeRawPointer($0) }
        return rawPointer.assumingMemoryBound(to: T.self).pointee
    }
    return nil
}

func isEnum<T>(type: T.Type) -> Bool {
    let metadata = unsafeBitCast(type, to: UnsafePointer<EnumMetadata>.self).pointee
    return metadata.kind == 1 // kind value of enum is 1
}

メモリレイアウトからEnumのスタブはInt型の値をキャストすれば手に入れられることが分かっています。しかし、その方法が使えるのはenumだけなので、スタブする型がenumの型であるか、ということをチェックする必要があります。そこでメタデータの出番です。メタデータの先頭アドレスにはその型がclassなのか、structなのか、enumなのか、といったkindの値が入っています。enumのkind値は1で固定なのでこの値を比較すれば型がenumかどうかを調べることができます。

Caution

  • ABI stability
  • Responsibility

Swift4.2ではABIが安定していないため、Swift5ではメタデータのレイアウトが崩れます。しかし、喜ばしいことにSwift5でABIの安定性が達成されました。今後は気軽にメタデータを使ったライブラリを作ることができます。ただし、Swift 4と5の両方をサポートしたい場合は維持が大変でしょう。 私たちは大いなる力を手に入れましたが、メタデータを使うことはSwiftのオフィシャルな方法ではないことを覚えておいてください。メタデータを使う時、特に書き換える場合は、メタデータについて正しい知識を持っておく必要があります。

Caution

  • ABI stability
  • Responsibility

例えば、前に紹介したMethod swizzlingはSwiftの型による最適化によって元の実装に戻ってしまうことがあります。実装を入れ替えるにはテーブルを参照してメソッドを実行する必要がありますが、Devirtualizeという最適化によって動的呼び出しが静的呼び出しになってしまうのです。 魔法を使うにはこのようなケースを正しくハンドルしなければなりません。 「大いなる力には大いなる責任が伴う」のです。

Summary

  • Swift uses metadata for dynamic behavior
  • We can use metadata in Swift
  • Let's write meta programming libraries!

要点をまとめます。 - まず、Swiftは動的メソッドディスパッチやリフレクションのAPIなどにメタデータを内部的に使用しています。 - また、メモリレイアウトを再現することでSwiftでメタデータを使うことができます。とても役に立つだけでなく、単純に楽しいです。 皆さんのメタデータを使った素晴らしいライブラリを待ち望んでいます。 以上で終わります。ありがとうございました。

18になった

18歳になりました。1年間の振り返りと気持ちを書いておきます。

17歳

やったこととしてはこんな感じ。

  • iOSエンジニアのバイト
  • 勉強会参加
  • カンファレンス登壇
  • コンパイラいじり
  • 卒論
  • OSS開発
  • アニメ鑑賞

自分としては結構充実していたと思ってます。が、行き当たりばったりにやっていたので計画を立てていたらもう少し色々出来たかもなーという思いもあります。

バイト

スタートトゥデイテクノロジーズでのアルバイトはめちゃめちゃ楽しかったです。隣でコンパイラの穴を見つけてボコボコに壊してる人がいたり、AndroidエンジニアとRxの話ができる環境は楽しすぎました。あと、Type Orientedな設計のお陰でパチっと機能追加できた、みたいなことがたまにあって体験が良かったです。

元々複数人での開発を経験してみたいというモチベーションで始めたので、これは実績解除できました。

勉強会

主にわいわいswittcに参加してました。5月の#2くらいまでは興味はあるが話を聞いても「ふむふむ分からん」状態だったのでフワフワ聞いてました。7月に入ってからコードを読み進めながら勉強会のアーカイブ動画を見るというスタイルで勉強してみました。自分の勉強した範囲と発表のテーマの知識を補完し合いながら読んでいくのは効率が良かったのでオススメです。

カンファレンス登壇

これ。 iOSDCで登壇したよ - kateinoigakukunのブログ

登壇をきっかけに僕が一方的に認知していた人から認知してもらえたり、「あ、あの人か!」と言ってもらえることが増えました。あとフォロワーもチョット増えました。

卒論

iOSDCで話した内容で書いてます。

OSS開発

主にInterface Builder用のLinterのIBLinterを作ってました。 GitHub - IBDecodable/IBLinter: A linter tool for Interface Builder

ポコポコPRやissueが立ったりして嬉しいなーという感じです。オーガニゼーション作ろうぜ!って言ってくれる人とかオーガニゼーションのアイコンを作ってくれる人が出てきて、インターネットって凄い!

18歳

重いので反転。

17歳の反省ポイントは「自己肯定感の低さ」なので、これをどうにかしていきたいです。 自意識過剰かもしれないですが、最近自分の年齢と興味の範囲のニッチさによって、周囲から過剰に評価されている気がしています。 評価と現実が乖離していて辛い〜って感じです。 18歳はそこの乖離を正しく認めることから始めたいと思います。

今月の終わりからバイト先がメルカリになります。今最高に楽しいらしいので楽しみです。 やっていくぞ💪

アレ

http://amzn.asia/hs44Fgt