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

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

ただいまの
回答率

89.98%

UICollectionViewを使ったカメラロールの表示 スクロールでmemory warningが起きる

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,859

gami

score 15

前提・実現したいこと

[iOS swift]UICollectionView、PHAssetを使ったカメラロールの表示
セルにカメラロールの画像を入れこむことは下記のコードで実現できたのですが、メモリの解放がうまくできません。

発生している問題・エラーメッセージ

画像が1000枚程度カメラロールにあり、初期表示は問題ないが素早くスクロールをするとアプリケーションが落ちてしまします。
正しい表示の方法、メモリの解放等ご教授いただけたらと思います。
よろしくお願いします。

Received memory warning.

該当のソースコード

import UIKit
import Photos

class albumViewController2: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate  {

    var photoAssets = [PHAsset]()
    var photoImages = [UIImage?]()
    var photoCounts:Int = 0
    var collectionView:UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let screenWidth = CGFloat( UIScreen.mainScreen().bounds.size.width)

        self.view.backgroundColor = UIColor.grayColor()

        //カメラロールから画像取得
        getAllPhotosInfo()

        //レイアウト部
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.scrollDirection = .Vertical
        flowLayout.minimumInteritemSpacing = 0.0
        flowLayout.minimumLineSpacing = 0.0
        flowLayout.itemSize = CGSizeMake(screenWidth / 4, screenWidth / 4)

        // コレクションビュー作成
        collectionView = UICollectionView(frame: view.frame, collectionViewLayout: flowLayout)
        collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        collectionView.dataSource = self
        collectionView!.delegate = self
        collectionView.backgroundColor = UIColor.clearColor()
        view.addSubview(collectionView)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }


    func getAllPhotosInfo() {
        photoAssets = []

        // ソート条件を指定
        let options = PHFetchOptions()
        options.sortDescriptors = [
            NSSortDescriptor(key: "creationDate", ascending: false)
        ]

        // 画像すべてのAssetを取得
        let assets: PHFetchResult = PHAsset.fetchAssetsWithMediaType(.Image, options: options)
        assets.enumerateObjectsUsingBlock { (asset, index, stop) -> Void in
            self.photoAssets.append(asset as! PHAsset)
        }
        photoCounts = photoAssets.count
    }


    //コレクションビュー
    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return photoCounts
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as UICollectionViewCell

        cell.backgroundColor = UIColor.brownColor()
        //PHAseetから画像取得のリクエストを投げてcellにセット
        let asset = photoAssets[indexPath.row]
        var photo: UIImage?
        let manager: PHImageManager = PHImageManager()


        for subview in cell.contentView.subviews{
            subview.removeFromSuperview()
        }
        manager.requestImageForAsset(asset,
            targetSize: CGSizeMake(CGFloat(asset.pixelWidth) , CGFloat(asset.pixelHeight) ),
            contentMode: .AspectFill,
            options: nil) { (image, info) -> Void in
                photo = image!
        }

        let imageview:UIImageView=UIImageView(frame: CGRectMake(1, 1, (self.view.bounds.width / 4) - 2, (self.view.bounds.width / 4) - 2 ))
        imageview.image = photo
        imageview.contentMode = .Center
        imageview.clipsToBounds = true
        cell.contentView.addSubview(imageview)

        return cell

    }
}

補足情報

Xcode 7.2.1 swift

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • fuzzball

    2016/07/25 16:51

    ゆっくりスクロールさせると落ちないんでしょうか?

    キャンセル

  • gami

    2016/07/25 16:59

    閲覧ありがとうございます。
    ゆっくりスクロールさせると落ちません。一画面に28セル表示させていて、勢い良くスクロールさせるとカクついてmemory warningが起きます。

    キャンセル

  • fuzzball

    2016/07/25 17:07

    画像リクエストの処理を無くしても同じようになるでしょうか?具体的には、「//PHAseetから画像取得の〜」から「photo = image! }」までコメントアウト。「imageview.image = photo」もコメントアウト。

    キャンセル

  • gami

    2016/07/26 09:37

    ご指摘のように画像リクエストをコメントアウトしたところ症状は出ませんでした。

    キャンセル

回答 1

checkベストアンサー

+1

書き逃げしておきます。

requestImageForAsset()を放置しているのが原因じゃないかなぁと思っています。

Subviewに関しては、removeFromSuperviewでひっぺがして処分していますので、それと同じようにrequestImageForAsset()も処分してやって下さい。リクエストしたときにrequestIDを保存しておいて、cancelImageRequest(requestID)してやればいいと思います。

他の原因、もしくは、他にも原因があるかも知れませんが。

 ただのメモリ不足?

大きな画像を大量に読み込んでメモリ不足になっているのではないか?という推測。
小さいサイズでリクエストするようにします。(requestID関係は一旦削除していいかも)

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    :
    //imageviewにはcellのサイズを渡す
    let imageview:UIImageView = UIImageView(frame: CGRectMake(1, 1, CGRectGetWidth(cell.frame)-2, CGRectGetHeight(cell.frame)-2))
    //imageview.image = photo //ここでは設定しない
    imageview.contentMode = .ScaleAspectFit //お好みで
    imageview.clipsToBounds = true
    cell.contentView.addSubview(imageview)

    //リクエスト
    manager.requestImageForAsset(
        asset,
        targetSize: imageview.frame.size, //imageviewのサイズでリクエスト
        contentMode: .AspectFit, //imageviewに合わせる?
        options: nil
    ) { (image, info) -> Void in
        //imageviewにセット
        imageview.image = image //読み込んだ画像をセット
        let degraded = (info![PHImageResultIsDegradedKey]?.boolValue)!
        //print(image!.size, degraded)
        if degraded {
            //print(indexPath, "continue...")
        }
        else
        {
            //print(indexPath, "done")
        }
    }

    return cell
}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/07/26 16:14

    なるほど 非同期でリクエストされると同時に配列にリクエストIDを追加していき、リクエストが消えないようなら消すということですね。

    追記回答を参考に書き直した所、memory warningはなくなりました。ありがとうございます。しかしながら、requestImageForAssetをしてからimageviewにimageをセットしていますが、非同期ゆえにか画像がセットされないセルが多々見受けられるようになりました。
    別の問題になりますが、ここにコードを記します。
    ```swift
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as UICollectionViewCell
    let asset = photoAssets[indexPath.row]
    let manager: PHImageManager = PHImageManager()
    let imageview:UIImageView=UIImageView(frame: CGRectMake(1, 1, CGRectGetWidth(cell.frame)-2, CGRectGetHeight(cell.frame)-2))

    for subview in cell.contentView.subviews{
    subview.removeFromSuperview()
    }
    cell.backgroundColor = UIColor.brownColor()
    imageview.contentMode = .ScaleAspectFill
    imageview.clipsToBounds = true
    cell.contentView.addSubview(imageview)


    //画像取得リクエスト
    manager.requestImageForAsset(asset,
    targetSize: imageview.frame.size,
    contentMode: .AspectFill,
    options: nil) { (image, info) -> Void in

    imageview.image = image

    }


    return cell

    }
    ```

    キャンセル

  • 2016/07/26 16:24

    コードはここに書かないで下さい。
    あと、質問内容が変わるのであれば新規に質問して下さい。

    キャンセル

  • 2016/07/26 16:47

    承知しました。ありがとうございました。

    キャンセル

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

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