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

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

ただいまの
回答率

87.49%

UICollectionViewを縦に高速でスクロールしてたら、メモリ関連のエラーでアプリが強制的に落ちる。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,715

score 16

[行いたいこと]
'Message from debugger: Terminated due to memory issue'というエラーメッセージが出て、アプリが落ちるのですが、どのようにしたらエラーを防げますか?

[発生した問題]
UICollectionViewを上下に高速でスクロールしていたらアプリが落ちる

イメージ説明

[エラーメッセージ]
Message from debugger: Terminated due to memory issue

[ストーリーボード]
UICollectionViewを実装し、cellの中にはimageviewとViewとViewの中にラベルが2つ入っています。
イメージ説明

[メモリ]
xcodeのMemoryと書いてあるところをみると、スクロールしてる最中のメモリは、
200MB前後ぐらいから280MB前後ぐらいまでになります。
イメージ説明

[使っているデバイス]
実機のiPhone6

Postクラス

class Post {

    var id: String?
    var timestamp: Int?
    var imageUrls = [String]()

}


extension Post {
    static func transformPostPhoto(dict: [String: Any], key: String) -> Post {
        let post = Post()
        post.id = key
        post.timestamp = dict["timestamp"] as? Int
        let image0 = dict["image0"] as? String
        let image1 = dict["image1"] as? String
        post.imageUrls.append(image0!)
        post.imageUrls.append(image1!)
        return post
    }
}

UICollectionViewDataSourceと、UICollectionViewDelegateFlowLayoutを継承したクラス

//100個ほど入ってる。
  var posts = [Post]()

//追記
override func viewDidLoad() {
        super.viewDidLoad()

        var REF_POSTS = Database.database().reference().child("posts")
        REF_POSTS.queryOrdered(byChild: "timestamp").observeSingleEvent(of: .value) { (snapshot) in
            if let arraySnapshot = (snapshot.children.allObjects as? [DataSnapshot])?.reversed() {

                arraySnapshot.forEach({ (child) in
                    if let dict = child.value as? [String: Any] {
                        let post = Post.transformPostPhoto(dict: dict, key: child.key)

                        self.posts.append(post)
                    }
                })

                self.collectionView.reloadData()
            }
        }
    }

  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return posts.count

    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
    {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PostCollectionViewCell", for: indexPath) as! PostCollectionViewCell


        cell.post = self.posts[indexPath.item]
        return cell


    }

   func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 2
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.size.width / 2 - 1, height: (collectionView.frame.size.width / 2 - 1) + 76)
    }

Cellのクラス

class PostCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var photo: UIImageView!
    var savePhotoUrl: URL?

    var post: Post? {
        didSet {
            updateView()
        }
    }

//修正&追記
    func updateView() {
        print("Thread.isMainThread ", Thread.isMainThread) // true
        print("Thread.current ", Thread.current) //<NSThread: 0x281e91d40>{number = 1, name = main}
        if let photoUrlString = post?.imageUrls[0] {

            let photoUrl = URL(string: photoUrlString)

            // 読み込み(新規)
            if savePhotoUrl == nil {
                self.photo.sd_setImage(with: photoUrl) { (image, error, SDImageCacheType, url) in
                     self.savePhotoUrl = url
                }
            }

            // 何もしない(キャッシュ)
            else if savePhotoUrl == photoUrl {
                print("何もしない(キャッシュ)")
            }
            // キャンセル処理後に読み込み
             else if savePhotoUrl != photoUrl /*&& 読み込み中*/ {
                self.photo.sd_setImage(with: photoUrl) { (image, error, SDImageCacheType, url) in
                     self.savePhotoUrl = url
            }
            // (必要なら解放処理後に)読み込み
            else if savePhotoUrl != photoUrl  {

            }

           //     if self.savePhotoUrl == photoUrl {

             //       self.photo.sd_setImage(with: photoUrl)
              //  } else {
              //      print("一致しないので読み込みキャンセル")
              //  }
        }
     }
}

メモリ関連のエラーであることはわかるのですが、
何が原因(ストーリーボードなのかコードなのかそれ以外なのか)でアプリが落ちてるのかわかりません。どなたかご教授いただけたら幸いです。

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • fuzzball

    2018/10/11 09:11

    まずは画像の読み込みをやめて落ちるかどうかの確認ですね。で、非同期で画像を読み込んでいるんだと思いますが、読み込んでいる最中に再度リクエストされることは想定していますか?(キャンセル処理が出来ているかどうか、など)言い換えると、セルの再利用は考慮されていますか?ということです。

    キャンセル

  • kou009

    2018/10/11 10:37 編集

    ありがとうございます。画像の読み込みをやめてみるとアプリは落ちなくなりました。スクロールしてもメモリは、50から60MB程度に下がりました。 キャンセル処理についてですが、合ってるか自信ないですが書いてみました。質問文の方を修正いたします。でも、まだMessage from debugger: Terminated due to memory issueとエラーが出てアプリがおちてしまいます。

    キャンセル

回答 1

checkベストアンサー

0

追記修正欄では書きづらいのでこちらに書きます。

savePhotoUrlが、

  • nil → 読み込み(新規)
  • == photoUrl → 何もしない(キャッシュ)
  • != photoUrl&読み込み中 → キャンセル処理後に読み込み
  • != photoUrl&読み込み済 → (必要なら解放処理後に)読み込み

みたいな感じになると思うのですが。

Postってライブラリでしょうか?
まともなライブラリなら、この辺は勝手にやってくれそうな気もしますが‥。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/10/11 12:07

    Postクラスは自作のカスタムクラスになります。質問本文に追記いたしました。あと、UICollectionViewDataSourceを継承したクラスのviewDidLoad()も追記いたしました。viewDidLoadで、データベースからデータをとってきて、var posts = [Post]()にappendしています。あと、僕が書いたsavePhotoUrlの変数に値を入れるタイミングは合ってるでしょうか?回答いただき、書き直しましたが、画像が表示されなくなってしまいました。

    キャンセル

  • 2018/10/11 13:03 編集

    >>savePhotoUrlの変数に値を入れるタイミング
    判定する前に上書きしちゃったらダメでしょう。

    【編集】後半削除しました。

    キャンセル

  • 2018/10/11 14:15

    確かにこれだと意味ないですね。失礼しました。setImageのcompletionBlockで入れるということですかね。やってみます。ありがとうございます。

    キャンセル

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

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

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

  • トップ
  • iOSに関する質問
  • UICollectionViewを縦に高速でスクロールしてたら、メモリ関連のエラーでアプリが強制的に落ちる。