質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.78%

[Swift]メモリ解放の方法

受付中

回答 5

投稿 編集

  • 評価
  • クリップ 6
  • VIEW 22K+

score 54

Swiftでメモリを解放する方法を教えてください。

ある処理をクラス化しているのですが、処理が行われるたびにメモリが増えていきます。何度も繰り返していると1GBまでメモリの使用量が増えていき、アプリが落ちてしまいます。

初心者なので詳しくは分かりませんが、生成されたインスタンスが破棄されたままになっているのではと推測しています。

ちなみにこれが起こる処理はふたつあります。
前提としてTableViewでRSSを表示するアプリです。

①ページ遷移先のデザインを別にクラス化しており、セルが選択されるたびにそのクラスのインスタンスが生成される

②表示切り替えボタンを押すと、TableViewの表示方法が変更される。その表示を切り替えるたびに、一度ViewControllerを消して再描画している

この二つのときにメモリの消費が激しく、アプリが落ちてしまいます。
autoreleasepoolを使用してもうまくいきませんでした。

説明が下手で理解しづらくて申し訳ありません。

思い当たる方法があれば、ご教示いただけると光栄です。
よろしくお願いいたします。

 追記


ソースコードを追加しました。

// WebviewController.swift

class WebviewController: UIViewController, UIWebViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let screenwidth  = self.view.bounds.size.width
        let screenheight = self.view.bounds.size.height

        let urlString   = NSURL(string: "http://hogehoge.com")
        let urlRequest  = NSURLRequest(URL: urlString!)
        
        var webview = UIWebView()
        webview.frame = CGRectMake(0, 0, screenwidth, screenheight - 45)
        webView.delegate = self
        webView.loadRequest(urlRequest)

        let toolbar = Toolbar()
        self.view.addSubview(toolbar.makeToolbar()) 
    }

// Toolbar.swift

class ToolBar: UIView {

    func makeToolvar() -> UIToobar {
        // Toolbar作成
        let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: screenWidth, height: 45.0))
        toolbar.backgroundColor = UIColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1.0)
       return toolbar
    }

}

上記のWebviewControllerが開くたびに、ToolbarControllerのインスタンスが生成されます。
WebviewControllerを閉じてもToolbarControllerのインスタンスが破棄されていないのか、メモリが増えていって困っています。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • RioNishino

    2015/06/17 09:09

    ①の処理はToolbarクラスの件です。ToolBarクラスのインスタンスのせいか分からないですが、メモリが増えているのが現状です。そう考える理由は、ToolBarをクラス化せずにWebviewControllerに直接記述するとメモリが増えないからです。

    キャンセル

  • Stripe

    2015/06/17 21:49

    隠さずにコードを書いてください。まだまだ、書いていないコードが沢山ありますよね?

    キャンセル

  • RioNishino

    2015/06/18 08:55

    何も隠してはいないのですが、何が必要なのかがわかないというのが正直なところです。本当にそれに関しては申し訳ありません。何度も正確に書け、隠さずに書け、と言われるということは問題はここには無いということでしょうか? そうすると思い当たるのは、Webviewを表示するときにmodalPresentationStyleでカスタムクラスを利用しています。そのインスタンスが破棄されていないということでしょうか?

    キャンセル

回答 5

0

addSubViewしたviewを削除する場合、removeFromSuperview()を呼ぶ必要があるようです。
WebviewControllerを閉じるタイミングで

    var subviews = self.subviews
    for subview in subviews {
        subview.removeFromSuperview()
    }
のような処理を入れてやる必要があると思います。
また、ToolBarクラス(ToolbarControllerクラス?)が破棄されるタイミングでも同様にaddSubViewされているUIToolbarもremoveFromSuperviewを呼ばないといけないと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/06/12 12:34

    回答ありがとうございます。

    removeFromSuperview()はインスタンスも破棄されるものなのでしょうか?
    試してみたところ、インスタンスが破棄されている様子がありません。

    キャンセル

  • 2015/06/17 11:31

    ビューのリンクが外れるだけですね。
    インスタンスは破棄されないと思いますよ。

    キャンセル

0

ToolBarControllerといいつ、ToolBar(UIViewのサブクラス)しかないので、  
実際の所どう動いているのかわかりませんが、WebviewControllerは、  
webviewとtoolbarが貼り付けられているviewのコントローラという認識で回答します。  
  
まず、Toolbarがメモリリークを起こしていたからといって使用メモリが1GBになるということはそうそうありません。

おそらくWebViewControllerがメモリリークしているか、WebViewのメモリが上手く開放されていないと考えれます。

オブジェクトが開放されるタイミングでdeinitという特別なところが呼ばれるので、  
そこでWebViewControllerが呼ばれていることを確認してください。  

class WebviewController: UIViewController, UIWebViewDelegate {

    deinit {
        println("WebviewControllerは正しく解放されました")
    }
}

WebView自身が開放されているか確認したい場合は、サブクラス化してdeinitを書くといいです。  
  
また、インスタンスを正しく開放していてもWebViewのコンテンツがおかしかったりして  
システム的にメモリリークする場合があります。  
  
その場合は、deinitで空のHTMLを読み込ませてメモリを解放すると解決する場合もあります。  
  
class WebviewController: UIViewController, UIWebViewDelegate {

    var webview: UIWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let screenwidth  = self.view.bounds.size.width
        let screenheight = self.view.bounds.size.height
        
        let urlString   = NSURL(string: "http://hogehoge.com")
        let urlRequest  = NSURLRequest(URL: urlString!)
        
        self.webview = UIWebView()
        self.webview.frame = CGRectMake(0, 0, screenwidth, screenheight - 45)
        self.webview.delegate = self
        self.webview.loadRequest(urlRequest)
        self.view.addSubview(self.webview)
        
        let toolbar = ToolBar()
        toolbar.makeToolbar()
        self.view.addSubview(toolbar)
    }

    deinit {
        self.webview.delegate = nil
        self.webview.stopLoading()
        self.webview.loadHTMLString("", baseURL: nil)
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/06/15 16:30

    回答ありがとうございます。
    また、丁寧に解説いただき感謝申し上げます。
    色々と勉強になりました。

    メモリが1GBまでいくのは、何度もWebviewを読んだ時です。

    教えていただいた方法を実行したところ、メモリ消費が大きく抑えられました。
    ありがとうございます。

    ただ、Webviewを呼ぶたびに20〜30MBほどメモリが上がるのですが、これは正常なことなのでしょうか? それともまだメモリ解放できていないということなのでしょうか?

    キャンセル

  • 2015/06/15 17:09

    WebViewは、キャッシュなどView以外のメモリも確保されるので、WebViewの画面が消えたからといって直ぐにメモリが解放されなかったりします。

    ただ、ページを開きなおした場合は、既に確保したメモリが使われるので、トータルのメモリ量はさほど増えないと思います。

    もしも、画面を閉じて開いてを繰り返した際に延々と20~30MB増え続けて減ることが無いのであれば、正しく解放されていないと思います。

    キャンセル

  • 2015/06/16 10:55

    繰り返し画面を開いたり閉じたりしていると、延々とメモリが増え続けます。クラス化せずにUIToolbarを直接WebviewControllerに記述すれば、メモリが増えることはありません。

    しかし、他の場所でも使うためクラス化しておきたいのです。

    ご回答、ご返信ありがとうございます。
    色々と模索してみます。

    キャンセル

0

self.view.addSubview(self.webview)
let toolbar = ToolBar()
toolbar.makeToolbar()
self.view.addSubview(toolbar)

この辺のも解放した方がよいかもです。
self.webview.removeFromSuperview();
toolbar.removeFromSuperview();
self.webview = nil;
toolbar.release();
toolbar = nil;

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

クラス化して使いたいとのことであれば、シングルトンでの設計をオススメします。
シングルトンとは、そのクラスのインスタンスがいつしかないことを保証する設計方法です。
クラス化する部分が、全て共通部分であるのなら、ToolBarのインスタンスは一つにして使い回すということができるはずですよ!

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

このタイプの問題が起きたときの一般的な対処方法で、まずは、トライしてみるのが良いと思います。いくつかに分けて書きます。

[1] 問題の絞り込み
どこがメモリリークの原因になっているかを絞り込んだ方が良いと思います。
まず、下記の2行をコメントアウトし、何度も実行してみて、問題が起きるかを確認してください。

let toolbar = Toolbar()
self.view.addSubview(toolbar.makeToolbar()) 

次に、2行目だけをコメントアウトして、同じ操作をし、問題が起きるかを確認します。
(インスタンス確保が問題か、追加することが問題か、そもそもToolBarクラスが原因かどうかの判別)

[2] 呼び出しタイミングの確認
上記のviewDidLoadが呼ばれるタイミングが意図した通りかを確認します。例えば、実行される度に、WebViewControllerの解放が無く、viewDidLoadが呼ばれていると、呼ばれる度にWebViewControllerが増えていきます。

その場合はWebViewControllerが解放される側の処理に問題があります。つまり、追記して頂いているコード以外の場所です。例えば、WebViewControllerを作って表示するところなど。

[3] ToolBarクラスのスーパークラスについて
コードを拝見すると、ToolBarクラスはUIToolBarを作って返すコードなので、ToolBarクラスがUIViewクラスのサブクラスである必要は無いと思います。予期しない処理が入り込む可能性を防ぐ意味でも、ToolBarクラスを直接配置するのでなければ、スーパークラス無しで良いと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る