NSURLSessionがメモリリークしてしまうのをなんとかした

  • 20
    Like
  • 1
    Comment

自社のWebAPIの通信にNSURLSessionを使って通信をしていました。
Debug memory graghをみるとNSURLSessionのインスタンスでメモリリークが起こっていました。
それを直した記事です

環境

  • Xcode8.3.3
  • Swift3.1
  • iOS10.3.2

問題点

NSURLSessionを使って通信処理をする実装をしていました。

let mySession = URLSession.init(configuration: URLSessionConfiguration.default)
//urlインスタンスはURL型
let task = session.dataTask(with: url, completionHandler: {
    data, response, error in

    // do something.
    })
task.resume()

Debug memory graghをみるとたくさんのメモリリークがありました。

20170614_1497403813.jpg

ほっとくとメモリリークでアプリが落ちるなど品質に問題が出てきてしまいます。
なんとかしたいです。

Backtraceのメソッドを見るとNSURLSessionのインスタンスにたどり着きました。

20170614_1497403851.jpg

mySessionが漏れている?
なんでだろうと色々調べるとNSURLSessionのインスタンスは専用のメソッドを実行しないと解放されないことがわかりました。

NSURLSessionの解放は専用のメソッドで行う

ドキュメントを見るとNSURLSessionのインスタンスはデリゲートに対して自分で解除するまで強参照で保持され続けるのだそうです

Important
The session object keeps a strong reference to the delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.

https://developer.apple.com/documentation/foundation/nsurlsession?language=objc

なので解除するメソッドを実行する必要があります。
解除するメソッドはいくつかあってそれぞれ違いがあるようです。

invalidateAndCancel

finishTasksAndInvalidate

flushWithCompletionHandler:

resetWithCompletionHandler:

NSURLSessionインスタンスのメモリリーク解消

今回は画像をアップロードする処理だったので、未処理タスクを完了させるfinishTasksAndInvalidateで解放してみました。

let mySession = URLSession.init(configuration: URLSessionConfiguration.default)
//urlインスタンスはURL型
let task = session.dataTask(with: url, completionHandler: {
    data, response, error in
    session.finishTasksAndInvalidate() // 追加
    // do something.
    })
task.resume()

completionHandlerの一行目に入れてみました。
すると

20170614_1497405301.jpg

なんということでしょう。
あんなにリークされていたNSURLSessionインスタンスが綺麗さっぱりなくなりました。

(のこっているリークはまた別な理由。)

さいごに

NSURLSessionインスタンスが勝手に消えると思い込んで2日間悩みました。
みなさんもNSURLSession使う時は解放のメソッドを忘れないようにしましょう。

参考