姑プログラマの技術日記

作ったものをえらそーに解説したら幸せになれるかな

PICOLで発見したこと。アセットカタログは計画的に利用したほうがいい

最近、iPhoneも性能が上がってあんまりメモリのこととか気にしないでガンガン作ることが多いのかも。

naochi-interrupt.hatenadiary.com

前回書いたようにPICOL(ピコル)-バーコードをピコってお金に変える”スピード買取”アプリはゲームっぽい楽しさを意識して作ったので、アニメーションとかとにかく多い。

まずはスプラッシュからアニメーションです。

ここだけの話ですが、国民的麦わら海賊団的な雑誌のアプリのスプラッシュがキラッとしてるのは私が作ったものです。

PICOLのスプラッシュはなんかかわいいので、よくあるAppDelegateのdidFinishLaunchingWithOptionsに一回挟んでおわり系ではなく、アプリをバックグラウンドに落として一定時間すぎてアプリをアクティブにしたらもう一度スプラッシュから始まるような作りにしています。

このアニメーションは、CABasicAnimationとか、そっち系のコードでアニメーションするのではなく、パタパタ漫画です。gifアニメーションみたいな、そういうの。

これはUIImageViewに配列でイメージを渡すという。ごくごく簡単なもの。 まあ、誰でもできる。

var imageList:[UIImage] = []
for i in (1 ... 35) {
     let image = UIImage(named: "splash-\(i)")
     imageList.append(image!)
}
imageView.animationImages = imageList

スプラッシュはアセットカタログに入れてあるpng35枚なので、このような書き方です。 ただ、メモリ量が大きいのです。使用メモリが多いと、なにかのきっかけでクラッシュしたりとか、動きがもっさりしたりとか、いやな動きが多くなります。 この感じでスプラッシュを作ってた時のメモリは恥ずかしながらこんなかんじ。 画像を最適化したり、色々やった後でこれ。

f:id:naochi_2012:20180621140850p:plain

このときは、アプリが動いている時ずっとこんな感じのメモリで続いてて、アプリを一旦バックグラウンドに落としてすぐフォアグラウンドに戻すとガクッとメモリが減る。

InstrumentsのLeaksとか見てもメモリリークはしてないし試しにスプラッシュやめて見たらメモリが減ったわけです。

この配列に入ってるイメージが解放されなくて残ってるんだな〜〜〜といろいろやりました。

autoreleasepoolいれたり。

autoreleasepool(invoking: {
    var imageList:[UIImage] = []
    for i in (1 ... 35) {
        let image = UIImage(named: "splash-\(i)")
        let image = UIImage(contentsOfFile: imagepath!)
        imageList.append(image!)
     }
     imageView.animationImages = imageList
})

アニメーション終わってから涙ぐましい解放したりして。StackOverflowに書いてあったし。

imageView.startAnimating()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
    UIView.animate(withDuration: 1.0,
        delay: 0.1,
        options: .curveEaseInOut,
         animations: { () -> Void in
        self.imageView.alpha = 0
    }, completion: { _ in
        self.imageView.image = nil
        self.imageView.stopAnimating()
        self.imageView.animationImages = nil
        self.imageView.layer.sublayers = nil
        self.imageView.removeFromSuperview()
})

全然ダメでした。

そしたら、急に思い出しました。そう言えばアセットカタログがなかった昔は、普通にイメージをフォルダで追加してたなって。 そして、業務用のアプリは こういう書き方あんましちゃいけなかったんだっけってのを。

UIIimage* image = [UIImage imageNamed:@"imagename.png"];

こう書く。そうするとイメージがキャッシュされないんだって。

NSString* path = [[NSBundle mainBundle] pathForResource:@"imagename" ofType:@"png"];
UIImage* image = [[UIImage alloc] initWithContentsOfFile:path];

ってことで、アセットカタログに入れてあったイメージを使わず、昔の通りにファイルで追加して、呼び出して見た。

var imageList:[UIImage] = []
for i in (1 ... 35) {
//   let image = UIImage(named: "splash-\(i)")
    let imagepath = Bundle.main.path(forResource: "splash\(i)", ofType: "png")
    let image = UIImage(contentsOfFile: imagepath!)
    imageList.append(image!)
}
imageView.animationImages = imageList

そしたら、驚きの結果が

f:id:naochi_2012:20180621140838p:plain

アセットカタログに入れるのは、何回も使うものだけにした方がいいんですね。つい便利なので文明の力に甘えきってしまっていて反省しました。

私にはすごい発見で、目からウロコがぼろぼろ落ちましたが、別にメモリなんかたいしたことないって? で?って思っちゃダメ。

ていうか、みんなガシガシアセット使ってるよね。