iOSDCをきっかけに「Swift5 async/await 研究読本」を購入して読んだ

ツイート

本の中身の前に

本の中身(ざっくり)

第1章 async/awaitとは何か

  • ネストの深いクロージャをasync/awaitを利用すること同期的でシンプルなコードとして表現できることについて説明しています。

第2章 Hydraで理解するasync/await

  • すでにSwiftでasync/awaitを実装しているライブラリ https://github.com/malcommac/Hydra の使い方についての説明しています。
  • Promise単独の例とasync/awaitを併用した例を並べることでasync/await構文がやっていることについて解説しています。
  • 確かJavascriptだとPromiseが先に入っていて、後からasync/awaitが追加されていたはずなので、これが自然な導入なんでしょうか。
  • エラーハンドリングについてもJavasciprtとおんなじ感じだった記憶があるので、あーそうそうそんな感じだよね、と自然に読めました。初見の人はどうなんだろう。

第3章 Hydraのasync/await実装を読む

  • 非同期処理の完了待ちをどうやって既存のSwiftの機能で実装しているかについて説明しています。
  • セマフォに尽きる、という印象でした。並行処理の待ちといえばこいつですよね。最近使ってなかったからこいつの存在を完全に忘れてた複雑怪奇な処理を実装するときにお世話になっていました。
  • Promiseのallは存在は知っていましたがzipなんてあったっけ?、と思ってMDNを調べたらjavascriptにはなくてああ動的型付けだからかと解釈しました。
  • googleはObj-cで実装したライブラリを公開している( https://github.com/google/promises )。恐ろしい。

第4章 「クリス・ラトナー氏が提案しようとし ている async/await for Swift のプロ ポーザルの下書きを解説する」

  • タイトルの通り、今回のasync/awaitの提案(プロポーザル)を和訳した上で説明しています。
  • 中身も大事ですがこういう感じで新機能を提案するのか、という意味でも勉強になりました。

雑感

  • ツイートもしましたが、すでにKotlin・JavascriptDartにはasync/awaitが入っているので、今回のSwift5で主要なネイティブアプリの言語でasyncが実装されることになります。Webを含めてフロントエンドプログラマの教養とか基礎知識として理解する必要性が大きいと思っています。
  • 会場で質問しましたが、Rxと競合している部分があるので置き換わりが発生すると思っています。通信系のライブラリや画像キャッシュライブラリでReactive Extensionsを作っているようなものは処理をasync/awaitで実装するだろうと見込んでいます。一方でRxCocoaを含むUIとのbinding系は置き換わらない気がするのでそこはRxを続けるかな、と考えています。

今知っていること

  • クラス
    • クラスは[[SomeClass alloc] init] とかSomeClass.init(with: value) みたいなイニシャライザでインスタンスを作る。
    • メソッドにはクラスメソッドとインスタンスメソッドがあり、後者はオブジェクトに備わっている
    • 昔の現場ではmybatis使ってるサーバ側に合わせて単なるマッパーでしかないモデルクラスを作っていて案の定コントローラが肥大し、途中でモデル側に最低限のメソッドを積むようにしていた。
  • 構造体
    • 構造体は値型、クラスは参照型
    • SwiftについてはCoWのため別のところに代入しても何か変更を加えなければメモリを消費しないらしい
  • 参照型はfor文とかでarrayの中身を取り出して処理していると気づいたら変わっているので好き
    • 知らないところで変わっていると死ぬ
    • objcはmutable型があったため区別は一応ついたしswiftは基本immutableで進められるから安心感がある
  • クロージャは仕事じゃないと絶対に(面倒で)使わなかっただろうけど今はこれなしで処理作れない(arrayの処理をfor文でなくブロック式でやるように指導してくれた先輩には感謝しかない)
    • [weak self] は正直なんとかしたい感はある
    • 油断するとネストが深くなり「これがコールバック地獄か・・・」という気持ちになれたので早くaysnc await欲しい
  • 関数プログラミングはよくわからないけどswiftでmap, reduce, etc. ができたおかげでtotalXXXな変数をmutableで宣言→すぐ下でfor文回すみたいなコードを作らなくてよくなってQOLが上がった。
  • main関数は特別
    • objcでも実はmain関数から始まっていた(クラッシュしたときに見ることが割とよくあった)。Swiftで最初から作ったアプリだと見ないなあ
  • テストは大事
    • 最初のところだと単機能の実装に対してexcelで100超えの項目書を作っていたがもうやりたくない
    • XCTest最高!ModelとViewModelなら境界値とシンプルなユースケースは網羅できて幸せ
    • 現実的なUITestってあるのかなあ
  • nil(null)の取り扱いは面倒臭い
    • Optionalは本当に助かってる。nilが入る可能性のない箇所と仕方なく入る箇所が切り分けられるのでコーディングに不安がないし、Javaぬるぽ踏んだので勘弁して欲しい
  • ポインタはわからない
    • objcでは雰囲気で使ったけどよく考えたらNSInteger系は値型だったからアスタリスクつけなくても良かったのかな?付けなくてもいいのにNSInteger *intValue にしたせいで不具合になっていた以前の現場を思い出した
  • メモリ管理よくわからない
    • ARC時代しか知りません(autorelease poolは稀に使った(もう覚えていない))
    • 稀にメモリリークすることがあり、古参の先輩がMRC時代の手動管理の話をしてくれたことがあった
    • objc, Swift はC言語系で参照カウント方式とかJava系はGCがあるという概念の話は聞いている

Swiftで旧漢字を判定したかった

できなかった

  • 方針その1: shift-jisに変換できるかどうかで判別する

参考: www.softel.co.jp

extension String {
    func containsOldKanji() -> Bool {
        return !self.canBeConverted(to: .shiftJIS)
    }
}

let str = "高"
str.containsOldKanji() // true

let str2 = "髙"
str2.containsOldKanji() // なぜかtrue

後々の試行錯誤中にshift-jis変換がうまくいっていることが判明し、IBM拡張なるものを知る
f:id:slowlylearner:20180104033139p:plain
文字コード表 シフトJIS(Shift_JIS)

  • shift-jis変換は成功しているので、IBM拡張の範囲(纊~黑まで)の漢字が含まれているかどうかを判定する

結果から言うとだめ

extension String {
    func containsOldKanji() -> Bool {
        //        return !self.canBeConverted(to: .shiftJIS)
        return containsIBMExtension()
    }
    
    
    func containsIBMExtension() -> Bool {
        guard let data = self.data(using: .shiftJIS),
            let shiftJISStr = String(data: data, encoding: .shiftJIS)
            else {
                return false
        }
        
        // http://charset.7jp.net/sjis.html のIBM拡張(?)で漢字相当の範囲を引っ掛ける
        let regEx = ".*[\u{ed40}-\u{eeec}]+.*"
        let test = NSPredicate(format: "SELF MATCHES %@", regEx)
        return test.evaluate(with:shiftJISStr)

上の後でパターンの方もshiftjisにしないとだめかなーとか漢字をダイレクトにパターンに入れてしまえばいいのではないかと思ったがダメだった。

extension String {
    func containsOldKanji() -> Bool {
        //        return !self.canBeConverted(to: .shiftJIS)
        return containsIBMExtension()
    }
    
    
    func containsIBMExtension() -> Bool {
        guard let data = self.data(using: .shiftJIS),
            let shiftJISStr = String(data: data, encoding: .shiftJIS)
            else {
                return false
        }
        
        // http://charset.7jp.net/sjis.html のIBM拡張(?)で漢字相当の範囲を引っ掛ける
        let regEx = ".*[纊-黑]+.*"
        guard let dataRegEx = regEx.data(using: .shiftJIS),
            let shiftJISRegEx = String(data: dataRegEx, encoding: .shiftJIS)
            else {
                return false
        }
        
        let test = NSPredicate(format: "SELF MATCHES %@", shiftJISRegEx)
        return test.evaluate(with:shiftJISStr)

最後の方は両方ともcontainsOldKanjiがtrueを返していたので「纊-黑」の範囲がshift-jisではなくutf-16とかunicodeとかで解釈されている可能性があり、そもそも何がどうなるとswiftの時代にshift-JISで正規表現を書くのだろうかという気持ちになった。

20181231

睡眠

4:49入眠 10:55起床
6:03時間睡眠

itっぽいこと

Type erasureでプロトコルのequatableを逃がすことができた

その他
  • 今月は36程度のZが発生していた。20くらいにしたい
  • 1年半経験の転でもやっていけることが判明した奇跡
  • elinをさわれた奇跡
  • 来年はもう少しましな生活が送れますように