try!swift workshop 'Testing and Performance Workshop'

 

try!swift 3日目のworkshopの一つである'Testing and Performance Workshop'に参加して来ました。

 

www.tryswift.co

 

会場はこちらでした。

www.google.co.jp

 

講師のSamuelさんがワークショップ用のリポジトリを作ってくれました。

github.com

 

パフォーマンスに問題のあるプロジェクトを直していこう!と言うものです。主に実施した2つを紹介していきます。

  1. ScrollingSmooth
  2. ImagesInTables

 

  1. ScrollingSmooth

※すでにmasterの最新は解決ずみのものが上がってますのでご注意ください

Initial commit with sample projects. · sgoodwin/PerformanceDemos@92fcf1e · GitHub

 

  • 問題発見

これを実行すると...真っ白😮

f:id:entaku19890818:20190323171330p:plain

これを調査します。

 

  • 調査

XcodeのメニューからProduct>Profileを開き"Time Profiler"を開きます

f:id:entaku19890818:20190323171656p:plain

 

すると....

f:id:entaku19890818:20190323171907p:plain

CPU100%😇

 

 

お気付きの方も多いかも知れませんが、10,000,000回appendしており、また全てメインスレッドで行なっているので、描画処理が進まないと言う状態です。

    override func viewDidLoad() {

        super.viewDidLoad()

        

        for _ in 0..<10_000_000 {

            data.append(randomWord())

        }

        tableView.reloadData()

    }

 

 

  • 解決

そこでswiftでの非同期処理を実現するために"DispatchQueue"を使います。

    override func viewDidLoad() {

        super.viewDidLoad()

        

        namesQueue.async { [weak self] in

            guard let self = self else { return }

            for _ in 0..<10_000_000 {

                self.semaphore.wait()

                self.data.append(self.randomWord())

                self.semaphore.signal()

            }

        }

        DispatchQueue.main.async {

            self.tableView.reloadData()

        }

    }

 

viewControllerの全体はこちらから

PerformanceDemos/ViewController.swift at master · sgoodwin/PerformanceDemos · GitHub

 

結果

f:id:entaku19890818:20190323175939p:plain

 

 

Time Profilerで見るとメインスレッドが空き、サブスレッドが張り付いていることがわかります。

非同期処理を行うことで「時間のかかる処理は別の場所で実行する」と言うことが可能になります。

f:id:entaku19890818:20190323180006p:plain

 

2.ImagesInTables

 

続いて名前から察してるかも知れませんがimageの読み込みです。

実行すると...カクカク😱

f:id:entaku19890818:20190323181733g:plain

 

  • 調査

CPU上ではさっきと違って100%と言うわけではありません。

f:id:entaku19890818:20190323182118p:plain

 

ですが、コードを見るとセルの表示時にURLからデータを読み込んで、表示しています...

  • URL先のdataを取る
  • とったdataをUIImage型へ変換し、imageViewへ設定する

ということを行うので、メインスレッドが混雑してカクカクになってると思われます。

 

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

        cell.imageView?.image = UIImage(data: try! Data(contentsOf: URL(string: "https://via.placeholder.com/150.png?text=Demo+desu!")!))

 

        cell.textLabel?.text = "row \(indexPath.row)"

        

        return cell

    }

  • 解決

またもや" DispatchQueue"さんですよ。

今回はちょっと工夫してデフォルトイメージを設定してみました。

    let defautImage:UIImage = UIImage(data: try! Data(contentsOf: URL(string: "https://via.placeholder.com/150.png?text=Demo+desu!")!))!

 

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let queue = DispatchQueue.global(qos: .default)

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

        cell.imageView?.image = defautImage

        queue.async {

            let data:UIImage = UIImage(data: try! Data(contentsOf: URL(string: "https://pbs.twimg.com/profile_images/828386643560181761/zVBAD623_400x400.png")!))!

            DispatchQueue.main.async {

                cell.imageView?.image = data

            }

        }

        

        cell.textLabel?.text = "row \(indexPath.row)"

        

        return cell

    }

 

結果

f:id:entaku19890818:20190323183245g:plain

サクサク😻

 

ほんとは画像キャッシュとかすると思いますが、時間がなかった(メンドくさい)ので解決です。

 

 

 

Time Profilerを見るとcellごとにスレッドできてますね。

(これはこれでいいのだろうか?)

f:id:entaku19890818:20190323183820p:plain

 

 

画像読み込み周りはいつもライブラリに依存してしまっているので、仕組みがわかってないと困った時に解決策を考えられないなぁと感じました。

 

 

 

その他画面遷移で画面が重なりすぎないようにするお話と、UnitTestをやったのですが、そこまで行かなかったので、このブログはここでおしまいです😃

 

 

まとめ

  • 重い処理はサブスレッドを駆使し、UIを更新するメインスレッドは空けておくような工夫をする
  • 画像取得とキャッシュをよろしくやってくれるライブラリは神。先人に感謝。
  • 自分のプロジェクトでTime Profiler開こう!どのくらい重い処理をしているのかしろう

 

 

以上です。みなさんも試してみてください!🤗