PICOLで発見したこと。アセットカタログは計画的に利用したほうがいい
最近、iPhoneも性能が上がってあんまりメモリのこととか気にしないでガンガン作ることが多いのかも。
naochi-interrupt.hatenadiary.com
前回書いたようにPICOL(ピコル)-バーコードをピコってお金に変える”スピード買取”アプリはゲームっぽい楽しさを意識して作ったので、アニメーションとかとにかく多い。
まずはスプラッシュからアニメーションです。
— なおち (@naochi___) 2018年6月21日
ここだけの話ですが、国民的麦わら海賊団的な雑誌のアプリのスプラッシュがキラッとしてるのは私が作ったものです。
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枚なので、このような書き方です。 ただ、メモリ量が大きいのです。使用メモリが多いと、なにかのきっかけでクラッシュしたりとか、動きがもっさりしたりとか、いやな動きが多くなります。 この感じでスプラッシュを作ってた時のメモリは恥ずかしながらこんなかんじ。 画像を最適化したり、色々やった後でこれ。
このときは、アプリが動いている時ずっとこんな感じのメモリで続いてて、アプリを一旦バックグラウンドに落としてすぐフォアグラウンドに戻すとガクッとメモリが減る。
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
そしたら、驚きの結果が
アセットカタログに入れるのは、何回も使うものだけにした方がいいんですね。つい便利なので文明の力に甘えきってしまっていて反省しました。
私にはすごい発見で、目からウロコがぼろぼろ落ちましたが、別にメモリなんかたいしたことないって? で?って思っちゃダメ。
ていうか、みんなガシガシアセット使ってるよね。