■開発環境
・Xcode 10.2.1
・Swift 5.0.1
■現象
タブAに以下のようにURLSessionを使って画像URLをUIButtonに表示する処理を書いたのですが、その後「タブBを選択」→「タブAを選択して再度画像を表示」を繰り返すと、メモリが微増していくではありませんか・・・。。メモリが解放されないのはなぜ・・・?
// コードで生成したUIButtonが入る private var buttons: [UIButton] = [] override func viewDidLoad() { super.viewDidLoad() self.showImages() } private func showImages() { let urls = [{画像URL1}, {画像URL2}, {画像URL3}] for i in 0 ..< urls.count { if let url = URL(string: urls[i]) { let configuration = URLSessionConfiguration.default configuration.requestCachePolicy = .reloadIgnoringLocalCacheData configuration.urlCache = nil let session = URLSession(configuration: configuration) let task = session.dataTask(with: url) { (data, response, error) in if let error = error { print(error.localizedDescription) return } guard let data = data, let response = response as? HTTPURLResponse else { return } if response.statusCode == 200 { DispatchQueue.main.async { let image = UIImage(data: data) buttons[i].setImage(image, for: .normal) } } else { print(response.statusCode) } } task.resume() } } }
■原因
Appleのドキュメントを見ると、「セッションオブジェクトは、アプリがセッションを終了するか明示的にセッションを無効にするまで、デリゲートへの強参照を維持します。セッションを無効にするまで、アプリケーションはメモリを消費します。」というような記載がありました。
https://developer.apple.com/documentation/foundation/urlsession
また、以下記事でもセッションを無効にすることでメモリリークを解決していました。
・https://qiita.com/SatoTakeshiX/items/b65f5518a025c23e1d9d
セッションを無効にしていないのが原因かも?
■解決
参考リンクの内容を元に、finishTasksAndInvalidate()を追加したところ、メモリが上昇する現象が解消できました!
private func showImages() { let urls = [{画像URL1}, {画像URL2}, {画像URL3}] for i in 0 ..< urls.count { if let url = URL(string: urls[i]) { let configuration = URLSessionConfiguration.default configuration.requestCachePolicy = .reloadIgnoringLocalCacheData configuration.urlCache = nil let session = URLSession(configuration: configuration) let task = session.dataTask(with: url) { (data, response, error) in // 以下処理を追加 session.finishTasksAndInvalidate() if let error = error { print(error.localizedDescription) return } guard let data = data, let response = response as? HTTPURLResponse else { return } if response.statusCode == 200 { DispatchQueue.main.async { let image = UIImage(data: data) buttons[i].setImage(image, for: .normal) } } else { print(response.statusCode) } } task.resume() } } }
■参考リンク
・https://developer.apple.com/documentation/foundation/urlsession
・https://developer.apple.com/documentation/foundation/urlsession/1407428-finishtasksandinvalidate