アプリのアップデート(Ver.1.6)をリリースすることになりました。前回のアップデートで内容の検証に抜けがあり、修正の必要があったためです。
Ver1.4まで当アプリの操作系には、再生中(デスクトップに表示中)のリストは、Finderとの同期や項目の削除はできないという制限を設けていました。再生中に項目に変更を加えようとすると、大変ややこしいことになろので、それまでは避けていたのです。しかしアンドゥ・リドゥの実装と同期機能の強化にあたり、そうも言ってられなくなったので、操作の制限を取っ払ったわけですが、操作系のコードの修正量もさることながら、動作のパターンが一気に増えて、検証しきれていない項目がありました。
ただ修正だけのアップデートではつまらないのでリリースをどうしようか考えていたところ、前々から自分で欲しかった四分割フリップのトランジションが二日で実装できたので、その流れでキューブ回転に挑戦したところ、何とか形にできたので、操作系の修正とトランジションの追加でアップデートとすることにしました。これでほんとにVer.1.xは完了となりそうです。
キューブはいずれは実装したかったトランジションだったので、今回で前倒しで実装することができました。もっとも代表的なトランジションの一つでもあるので、WWDCの中でもCoreAnimationのセッションでキューブ回転のデモがあったことを紹介する記事もあったりと、参考にできる記事は見つかるものの、例によって当アプリならではの事情もあって手こずりました。トランスフォーム(画像の変換)はパラメータの値や、ビューやレイヤーのセッティングに一つでも誤りがあると、結果として表示される画像が、まるで11次元の世界に迷い込んだかのような歪なものを見せられるので、その場では何をどうしたらいいのか途方に暮れてしまいます。
今回、実用性のあるトランジションを3種類追加したので、アプリのページに掲載するデモムービーも更新しました。デモムービーは前回初めて作ったのですが、動画編集ソフトのFinal Cut Proはその時初めて使い、今回で二度目。30秒のムービーなので半日程度でできるとはいえ、普段動画編集なんてしないので、使い方を探りながらの作業のせいでプログラミングより疲れました。
カテゴリー「プログラミング」の投稿
先日Macをスリープから解除したら、問題が発生したからと再起動を始めました。滅多にあることではないので普段は気にしないのですが、問題は再起動後、XCodeを立ち上げてDesktopShowアプリをbuild and runさせたらデータ読み込みができず初期状態に戻ってしまったことです。開発のさなか、動作テストの時にアプリのクラッシュに出くわすわけですが、アプリを再起動した際保存データが飛んでいて初期状態に戻ってしまうのが、開発中ずっと懸念事項でした。当アプリはイベントループの最後に大抵データ保存の処理をするのですが、アプリがクラッシュするのはビュー操作の過程であって、データアクセスの工程ではないのになぜデータが破損するのか、実はこのメカニズムが今でも掌握できていません。この一連の流れは全てメインスレッドで処理しているので、ビューがクラッシュした時点でデータ保存のルーチンまでは処理は進まないと思うのですが、実際はイベントループは最後まで処理され、ビューがクラッシュした時点でデータに不整合が生じているので、その状態でデータ書き込みが行われると、次回起動時にデータのデコードに失敗するのではないかと推察しています。
一つわかっているのは、このままでは困るということです。ビュー周りは大変複雑で、完璧にバグがないように作り込むことは不可能に近く、クラッシュは起こりうるものなので、何らかの対処法が必要と以前から考えていました。そこでビューの更新が行われないタイミングでデータのバックアップを取っておいて、クラッシュ後の起動時で正規のデータのデコードに失敗した場合、バックアップデータを読み込むという仕様をVer.1.5で加えました。実際開発作業中この方法でアプリのクラッシュに対処できることを確認しています。
そしてVer1.5をリリースした翌日に前述の事態に遭遇しました。もう呪われているのか、よりいいステップに進むきっかけに遭遇できたとポジティブに捉えるべきか、精神が試されます。システムが落ちたのは、おそらくXCodeが原因だと想像しているのですが、問題はアプリが正常に復帰できなかったことです。システムを巻き込むトラブルが発生した場合、今のやり方でもまだデータを失う可能性があることがわかりました。次回のアップデートの時には手動でデータを読み込む機能を追加しようと考えています。それ自体は以前から考えていた機能ですが、Ver1.5で実装した方法でその必要性はなくなったと思ったのですが、この問題は根が深く更なる探求が必要のようです。
今回アプリが正常にデータの復帰ができなかった際、コンソールに気になるメーっセージが出力されていました。こんなメーセージに遭遇するのは今回が初めてで、内容は/ver/以下のディレクトリにあるファイルへのアクセスに失敗したというものです。調べてみると/verはUNIXにおいて、雑多な物置的に使われているディレクトリのようで、アップルは/verに/ver/folders/という独自のディレクトリを追加して様々なアプリがこのディレクトリをキャッシュ領域として使っているようです。このディレクトリの中の方を覗くと~ユーザー名/Library/Containers/の中のように、様々なアプリのディレクトリがあり、その中に私のアプリのディレクトリcom.kissdgw.DesktopShowもありました。コンソールに出力されていたのはこの中の何ちゃら.plistが読めなかったというものです。ここまでの記述でお分かりかと思いますが、アプリ制作者はこんなコード書いてません。/ver/folders/なんてディレクトリの存在は今回初めて知りました。これらはシステム側がやっていることです。今回の原因がアプリではなく、システムもしくはXCodeと考える根拠です。でもまぁそんなことはどうでもいいのです。アプリ制作側は自分のするべきことをするだけです。とは言え、現在のOSはアプリ側が手の出せない領域が多くて複雑です。仮に手が出せたとして手を出す技量もないですし。
/ver/folders/ディレクトリで気になったのは、私のMacはその時このディレクトリが6GBもの容量を食っていたことです。そのうち実に5.9GB、ほとんどをcom.apple.desktoppictureすなわちシステムがデスクトップピクチャを表示しているプロセスが、表示した画像のコピーを書き込むことで消費していました。何という無駄なことをやっているんでしょう。ストレージ容量が256BG以下のMacだったら馬鹿にならない容量です。こんなところでストレージ容量を圧迫されているなんて一般のユーザーは誰も知らないでしょう。私も知りませんでいた。またこの仕様自体釈然としないものがあります。なぜ今更UNIXの領域なんかキャッシュに使っているのでしょうか。各アプリに標準で割り当てられている~ユーザー名/Library/Containers/でいいじゃないかと思うのですが。というか壁紙の画像なんかコピーしたってキャッシュとして何の意味もないでしょう。(正確にはjpegの実ファイルをpngに変換して書き込んでいるので、実ファイルの3倍以上容量増しになってます。壁紙として表示する画像なんかキャッシュする意味がそもそもないし。)
以上、今回遭遇した事件簿でした。
コメントする
※ 匿名希望の場合は空欄で構いません。
5回目のアップデート(Ver1.5)リリースです。今回をもってVer.1.xは終了です。開発の着手が2020年の5月末でしたから、およそ半年でVer.1.0のリリース、その後ひと月ペースで小刻みなアップデートを行い、一年後にVer.1.xの完了まで漕ぎ着けました。一年前昔買ったハウツー本を読み返すところから始めたことを振り返ると進捗のペースは上々だと思います。十数年前のプログラミングの勉強を一から始めた頃だったら、今の状態に作り上げるまで四、五年はかかっていたでしょう。
今回のアップデートの内容は、シャッフル再生とランダムなトランジション機能、Finderとの同期機能の強化、アンドゥ・リドゥの実装、前回までのリリースで気づけなかったバグ修正等です。もっと詳しく内容を語りたいのですが、ブログの文章を書いている時間がありません。細かいところでは、コレクションビューなどに改善したい要素もあるんですが、ビューの作り込みをするのがこのアプリの目的ではないので、気持ちを切り替えて次のステップに進もうと思います。
今回のバージョンでアプリの土台はほぼ出来上がりました。ここからは機能の拡張になります。これまで扱ってきたAPIは、AppKit、Fundation、CoreAnimation、CoreImage、CoreGraphics、他ちょこっとでしたが、いよいよここから現在のMacOSの特徴とも言える多彩な XX Kit系のAPIに進みます。読まなきゃいけないドキュメントもぐんと増えるでしょう。
バージョン2.0のリリースがどれくらいで出来るのか、今の時点では正直わかりませんが、今後も地道に頑張っていこうと思います。
コメントする
※ 匿名希望の場合は空欄で構いません。
4回目のアップデートリリースです。今回はしんどかったです。精神的にはVer.1.0リリースの時が一番プレッシャーがあり、その後のアップデートもきつかったはずですが、それらの記憶が薄らぐほどなんか今回はきつかったです。今回の内容は、CoreAnimationを使わないTimerを使ったトランジションをサブスレッド化することで、実用上メインスレッドと非同期の描画(厳密には違いますが)をさせることと、コレクションビューを使ったサムネイル画像による一覧表示の追加ですが、プログラミング的には今までで一番濃い内容になった分疲れました。今回のアップデートでいわゆる画像を閲覧するためのローダー・ビュアーと呼ばれるアプリに必要な要素をほぼ一通り備えたものになりました。見た目も機能も”製品”らしいものになってきたと思います。
思えば十数年前、独学でCocoaAPIを勉強し始めた時、作ろうとしていたのが自分の欲しい機能を備えた画像閲覧アプリでした。今ようやく当時目指していたレベルのものに辿り着いたような気がします。当時はコレクションビューもGrand Central Dispatchもなく、スレッドもビューコントロールも自分でコーディングしていました。そこそこ動くものを組み上げるところまでいってはいたのですが、結局完成に至らず頓挫してしまったのは、CocoaAPIのファイル操作系のAPIがプアで(ちょうどNSImageクラスがCoreGraphicsの代わりにならないように)、ファイル操作をするアプリを作るためにはCarbon Core APIに踏み込む必要があったためでした。ヘッダファイルだけで凄まじい物量のものをプリントアウトして、なんとか読み解こうと努力したのですが、純粋にC言語の世界なことに加え、ほとんど何の情報もないCarbon Coreは、C言語の経験がなくCocoa(Objective-C)からプログラミングの世界に入った初心者には、あの巨大なCのライブラリは荷が重すぎました。同様にC言語のライブラリであるCoreGraphicsも然りで、ファイル操作とグラフィックという画像閲覧アプリにとって要とも言える部分が、大規模なCのライブラリを使わないと実用的なものが作れないというのが、当時のCocoaAPIの実情だったのです。
あれから十年以上の歳月が経ち、今ではそのCarbon Core自体がDeprecated(API配布の終了)になっています。私がこのアプリ制作を決意して再びMacOSのAPIを学び始めた時、一番驚いたのがCarbon CoreがDeprecatedになっていることでした。時の流れを感じずにはいられません。
さて、残るはアンドゥ・リドゥとFinderとの同期機能が実装できれば、アプリとしての基礎機能はほぼ出来上がりますが、それをVer.1.5としてリリースするか、後回しにしてVer2.0に進めるか、今考えています。
コメントする
※ 匿名希望の場合は空欄で構いません。
三度目のアップデートをリリースしました。
短期間で一定の成果が上がったのと、それができるだけ速やかにリリースしたほうがいい内容だったため、予定外の緊急リリースです。当初考えていたスレッドまわりの見直しというのは、要はトランジションのアニメーションを非同期に行いたいためですが、CoreAnimationを使う限り、最初からレイヤのプロパティ設定ひとつで非同期描画をしてくれること、また仮想デスクトップ(Spaces)への対応もAppKitにその振る舞いを定義するプロパティがあり、それらを設定すれば目的の動作が実現できることがわかったためです。正直これらのことを知らずに今までのバージョンをリリースしていたのが痛恨の極みで、穴があったら入りたい気分です。また今回のバージョンで、他のアプリがフルスクリーン動作している時は壁紙切替の進行を止めるようにしました。というのもCIFilterを使ったモーフィング系のトランジシヨンが裏で走らせるにはリソース消費が無視できないものだったためです。私の使用環境(Core i7 6コア 2.2GHz)で、CUP負荷がシングルコア換算で70%前後、メモリ使用量が2.5Kモニタでも100MBくらい消費しますので、流石に止めないとまずいと思いました。CoreAnimationを使ったもののリソース消費は微々たるものなんですが。
今回のアップデートでミッションコントロールとの協調に関し、システムの行っている壁紙表示機能とほぼ同じ挙動ができるようになりました。モニタ構成の変更、システムのスリープと復帰、Spaces、フルスクリーンアプリ等、現在のMac環境で起こるだろう外部要因を大体網羅でき、それらが同時に絡んだ時、さらにトランジションアニメの最中にそれらが起こった時にもメモリリークを起こさずに動作するものに組み上げることができました。これでようやく、どなたに使ってもらっても日常的な使用で不便さを感じることのないものになったと思います。細かいところではまだ改善したい部分はありますが、緊急性をもった問題点は大体潰せたと思います。トランジションも10種類になり、私自身が日常的に使用して満足感を感じるものになってきました。
CoreAnimationを使わないトランジションの非同期描画がまだできないこと、テーブルビューのサムネイル描画がビジー状態になり、ユーザー操作を妨げるので使用感を損ねることが、次になんとかしたい課題ですが、これには時間がかかるでしょう。あと1、2回のアップデートでバージョン1.xは終了になると思います。
バージョン1.x世代はアプリとしての土台作りで、デスクトップを表示先としたメディアプレーヤーという当アプリのコンセプトを実装し、価格以上の価値を与えられるのはバージョン2.0からと当初から考えていますので、先は長いですが地道に取り組んでいきたいと思います。
コメントする
※ 匿名希望の場合は空欄で構いません。
2回目のアップデートをリリースしました。
当初はスレッドまわりの見直しに取り掛かるつもりだったのですが、Ver.1.1のアップデートをリリースした直後にメモリーリークのバグが見付かってため、予定変更してバグ修正とトランジション追加のアップデートです。
バグが発覚したきっかけは、amazon prime videoで映画を観終わった後に、アプリの動作が重くなっていたので、状況を確認してみたら使用メモリが4GBにもなっていたことでした。盛大にメモリーリークが起きていたのです。検証してみたところ、他のアプリがフルスクリーン動作をしているときにメモリリークが起こることがわかりました。
これには二つの要素が絡みます。まず当アプリでは、他のアプリがフルスクリーン動作をしている時でも壁紙切替の進行を止めていないこと。システムの壁紙表示機能は、フルスクリーン動作のアプリがある場合には進行を止めています。背後で壁紙切替をしてもリソースの無駄にしかならないので本来はこれが正解だと思われますが、リソースの消費は微々たるものなので、それを無視すれば止めても止めなくてもどちらでも構わないというのが実情です。なので当初迷ったのですが、将来的に環境設定で選べるようにするとして、当面は続行させる仕様としたのです。止めても止めなくてもどちらでもいいのなら、手順の少ない方にしたわけです。これがまず第一点。
次に、当アプリで大容量のメモリリークを引き起こす要因は、表示の終わった画像のViewの削除漏れしかないのは、制作の段階からわかっていました。当アプリでは、現在表示中のViewと、次の画像を表示させるViewの二つしかViewを作らないので、表示切替が終わったら前の画像のViewを決め打ちで削除するコードを書いていました。一抹の不安はありましたが、コードの進行上三つ目のViewが作られることなどありえないはずと考えていましたが、そこに落とし穴があったようです。
なのでメモリリークを引き起こしている箇所はわかっていたので、コードの修正はすぐにできたのですが、どういう過程を経てViewの削除漏れが発生するのか、そのプロセスがわかりませんでした。
当アプリの表示切替の進行の流れは大雑把に次のようになっていました。
- (void)キュー実行メソッド { ⬅️ タイミング 1
表示切替メソッド呼び出し
//次の表示の遅延実行
[self performSelector:@selector(キュー実行メソッド)
withObject:添付オブジェクト
afterDelay:インターバル];
} //このメソッドを抜けた後に遷移エフェクトが始まる
- (result)表示切替メソッド: {
現在表示中(前の)のView = ・・・確保
次の画像を表させるView = 画像を読み込んで生成
次の画像を表させるView を デスクトップに追加
//遷移エフェクトの開始
[CATransaction begin];
[CATransaction setCompletionBlock:^{ ⬅️ タイミング 2
//遷移エフェクトの後処理
前のViewの削除
}]:
switch (トランジションのタイプ)
{
case タイプ1:
{
アニケーション設定
[前もしくは次のView.layer addAnimation:設定したアニメーション];
break;
}
case タイプ2:
・
・
}
[CATransaction commit];
}
キュー実行メソッドの中で、表示切替メソッドと次のキュー実行メソッドの予約を同時に行っていますので、切替間隔の時間の中に遷移エフェクトにかかる時間も含まれます。そのため、アプリ側の予約設定として最短の表示時間を5秒、遷移エフェクトの最長時間を4秒にしていました。
コードのあちらこちらを色々検証してみた結果、他のアプリがフルスクリーン動作中は、ポイントとなる二箇所の実行タイミングが、システムによってルーズに呼び出されているのが原因だとわかりました。
その様子を視覚的に示しているのが、XCodeがアプリの使用メモリの時間経過を表示したグラフです。

システムがなぜこのような計らいをするのかはとりあえず置いといて、結果遷移エフェクトが終わって後処理のブロックが呼び出される前に、次のキュー実行メソッドが呼び出される場合があるのが、ロジックが狂う原因でした。
呼び出しのタイミングが遅延するのはどうでもいいことですが、処理の順序が前後するのは困るので、Viewを確実に削除する修正だけでなく、次のキュー実行メソッドの予約を遷移エフェクトの終了通知の呼び出しの中で行うよう変更しました。これによって壁紙の表示時間の中に遷移エフェクトにかかる時間が含まれなくなり、最短の表示時間と遷移エフェクトの最長時間の絡みもなくなりました。遅延実行のメソッドも利便性を考慮してタイマーオブジェクトに変更し、結果進行を管理しているコードにかなり手を加えることとなりました。
今回の予定外のアップデートは、いい意味で怪我の功名でした。Ver.1.0のリリース時はシステムのスリープとモニタ構成の変化に対応させるのに手一杯で、フルスクリーン動作への対応を後回しにしたせいで、今回の事態に遭遇し、おかげでアプリの動作への理解が深まり、コードも練り込むことができました。又、バグ修正だけのアップデートではつまらないので、もう少し先のことになる予定だったCIFilterを使ったアニメーションにも挑戦しました。ver.1.1で実装したアニメーションはレイヤーのプロパティ変更によるものに留まっていましたが、やっぱり遷移エフェクトといったらCIFilterを使ったものが本命なので、それを前倒しで実装できたのは大きかったです。
時間ができたらCIFilterを使ったアニメーションについても記事にしたいと思います。
コメントする
※ 匿名希望の場合は空欄で構いません。
▼ 記事の選択 ▼
最近の投稿
コメントする
※ 匿名希望の場合は空欄で構いません。
コメントを追加する キャンセル