16
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

tableVeiw.reloadData()をバックグラウンドスレッドで実行してはいけない

Last updated at Posted at 2016-08-01

Swiftでサーバから非同期にデータを取得して、それを画面に描画するという処理を書きたい。
はじめ、スレッドを意識せずに以下のようなコードを書いていたため、画面をタップしないとセルが描画されない、もしくは描画されるまで待っていると以下のようなエラーが出るという現象が起きていた。

エラー

This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes.  This will cause an exception in a future release.

問題のあるコード

loadData() {
    (task: AWSTask!) -> AnyObject! in

    if task.error !== nil {
        let alertView = UIAlertController(title: "エラー", message: "データの取得に失敗しました。", preferredStyle: .Alert)
        alertView.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
        self.presentViewController(alertView, animated: true, completion: nil)
    } else {
        self.tableView.reloadData()
    }
    
    return nil
}

ここでloadData()関数はAWS SDKを利用し、バックグラウンドスレッドで非同期にAWSからデータを取得する処理を担っているとする。loadData()はクロージャを受け取り、AWSと通信した後にそれを実行する。

通信が正常に終了した場合、self.tableView.reloadData()が実行されることになるが、この処理はバックグラウンドスレッドで実行される。
しかし、SwiftではUIに関する操作をバックグラウンドスレッドで行ってはいけない。そのため上記のようなエラーが出力されていた。

これを解決するため、以下のようなコードに修正する。具体的には、dispatch_async(dispatch_get_main_queue())を実行し、self.tableView.reloadData()をメインスレッドで実行するようにしてあげれば良い。

loadData() {
    (task: AWSTask!) -> AnyObject! in

    dispatch_async(dispatch_get_main_queue()) {  // 追加
        if task.error !== nil {
            let alertView = UIAlertController(title: "エラー", message: "データの取得に失敗しました。", preferredStyle: .Alert)
            alertView.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
            self.presentViewController(alertView, animated: true, completion: nil)
        } else {
            self.tableView.reloadData()
        }
    }  // 追加
    
    return nil
}

ちなみに、自分がメインスレッドにいるかどうかを以下のコードで調べることができる。

loadData() {
    (task: AWSTask!) -> AnyObject! in
    
    let currentThread = NSThread.currentThread()
    print(currentThread.isMainThread)  // false

    dispatch_async(dispatch_get_main_queue()) {
        let currentThread = NSThread.currentThread()
        print(currentThread.isMainThread)  // true
        
        if task.error !== nil {
            let alertView = UIAlertController(title: "エラー", message: "データの取得に失敗しました。", preferredStyle: .Alert)
            alertView.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
            self.presentViewController(alertView, animated: true, completion: nil)
        } else {
            self.tableView.reloadData()
        }
    }
    
    return nil
}
16
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?