はじめに
本記事は業界未経験のエンジニアが Delphi の FMX(FireMonkey)フレームワークを1年ほど触ってみて思ったこと、特にマルチプラットフォーム開発のなかで感じた反省などを1年の締めにポツポツと述べるポエム記事です。Delphi は駆け出しエンジニア向けの記事が少ないと思うので、初心者なりに書いてみようと軽い腰を上げました。
1月 ~ 4月目
エンジニアになりたての頃でまだ湯気が立ってます。そもそもの基礎知識が不足しています。「メモリ?ポインタ?クラス?」状態です。アプリケーション開発で必須の知識を身につけるためにこれらをはじめとする基礎知識の習得、Delphi の使用言語である Object Pascal の書き方を学習していきました。この段階でクラス設計やインスタンスの生成/解放などを実践しました。また、マルチプラットフォーム開発用のFMXフレームワークの基本的な使い方も簡易アプリを通して触れています。
印象的だったこと
- ライブラリのソースコードがユーザーに提供されている
→ デバグがしやすく、拡張性に優れる
個人的に Delphi の1番の長所だと思います
- パスカルケースであること
→ 例) MyName, TMyClass - 一般的な代入は "=" ではなく ":=" を用いる。等号は "==" ではなく、"="。
- Delphi の強みである GUI開発が発達している
→ 各コントロール(GUI上のパーツ)のプロパティやイベントを視覚的に設定できる
→ 開発効率や保守性が高く、プログラマの負荷が小さい - 各プラットフォーム向けの GUIフォームを簡単に切り替えられる
→ プラットフォームごとに記述するソースコード量が少ないため、プログラマの負荷が小さい
困ったこと
- インデントがズレるなどしてソースコードが見にくい
→ Object Pascal の書き方(スタイルガイド)を調べた - Delphi、特にFMX のユーザー数が少ないせいか情報源が限られている
→ 新規参入が容易でないと感じる - そもそも、1番の参考元である公式ドキュメントは一定のリテラシーを前提としていると感じた
5月 ~ 8月目
FMX のアプリ開発の感覚を掴んできた時期です。Object Pascal の書き方、クラス設計やインスタンス生成/解放の基本的な部分が身についてきました。。公式ドキュメントやライブラリのソースコードを読んで簡単な問題解決を行えるようになってきたところです。この時期から "動きのあるアプリケーション" を開発するという大きな課題に取り組みはじめました。
印象的だったこと
- アプリで使用する画像データやテキストファイルを各プラットフォームごとに簡単に設置できる
- 各コントロールのスタイルに関するライブラリが充実している
→ 簡単にアプリの見た目を切り替えられる
→ 初心者プログラマでも、ある程度の見た目のアプリを簡単に開発できると感じた
困ったこと
- マウスイベントを用いた連続的な計算処理で、動作がカクつく
→ 計算誤差が積み重なっていてたことが原因。たとえば除算を最後に行うことや、計算の基点となる値を設定するなどが必要
- アクセス違反やメモリリークが発生した
→ インスタンスの解放を忘れたり、すでに開放したインスタンスを参照したりしていたことが原因
→ FMX のコントロールにおいて親が子を開放するように、 "誰" が "どのタイミング" でインスタンスを管理しているかで悩んだ - メインフォームがあるユニットが大きくなり、保守性が悪くなった
→ 1つのユニットに多くのことを書き込むのではなく、"そこに書かなければならないこと" を "必要最小限" 記述する必要があった
→ たとえば役割が異なるクラス間でユニットを分けたりするのも有効
ここでいうユニットは、Delphiアプリケーションを構成するモジュールのことです
分けられたクラスは基本的にお互いを知らないようにしてクラス間の依存度を抑えます。
- Android/iOS などのデバイスに設置したファイルのパスを正しく参照できない
→ ストレージに保存されるファイルのパス構造はプラットフォームごとで異なる
Delphi には GetHomePathという便利なメソッドがあります。あとはメソッドの返り値に対してパスを結合するだけです。また、パスの区切り文字はプラットフォームによって "\", "/" で異なる可能性があるため注意が必要です。ご安心ください、Delphi では TPath.Combine メソッドが適切にパスを結合してくれます。そのほかにも DirectorySeparatorChar で適切な区切り文字を参照できます。
- アプリが重い
→ タイマーのイベント間隔が短すぎたり、複数のタイマーが重なったりするのは理想的でない。できれば少ないタイマーのイベント内で他タイマーに持たせたかった処理を併記するのがベターだと思われる
→ タイマーの間隔はOSごとに保障されている分解能に合わせて 50ms(1s = 1000ms) あたりを目安にした
9月 ~ 12月目
この時期は前述したアプリのデザインと動作を大きく修正しました。"ただ動く" 状態と "カッコ気持ちよく動く" とでは大きな差があります。そのためにアプリの動作をユーザー操作の一般的な認知に沿った自然なものにしつつ、UIデザインを一新する必要がありました。
社内のデザインチームさんと連携しつつ修正を重ねていきました。ここで課題になったのが "遊び手" と "作り手" の違いです。自分はゲームっ子だと思いますが、いざアプリ開発となると UI設計が思うようにいきません。デザインチームさんから「こうしたほうが自然ですよ」とご指摘されて 「なんでそこに気づかなかったんでろう」と800万回ほど繰り返し思いました。
普段から、ただ遊ぶのではなく、どのような UI設計なのだろうかという視点を持つ重要性を痛感しました。
印象的だったこと
- iOSアプリ開発に必要な認証などの設定が煩雑
→ デバグだけなら簡単にビルド・実行できる抜け道があるが、基本的に Apple Developer Program に登録する必要がある。やはり Android が最高... - 大量のファイルを設置するときは あらかじめ固めたzipファイルを解凍したほうが管理しやすい
- 設定ファイルに関するクラスのように1つ生成すればいいものはシングルトン(インスタンスの単一制限)で実装する
シングルトンのインスタンスを複数のスレッドから参照するときは排他的に行う必要がある
困ったこと
- 1つのクラスのソースコード量が大きくなってしまう
→ Delphi 特に FMX における GUIのオブジェクト(コントロール)が 大きく分けて GUI担当・モデル担当の2部構成であるように、GUI部分とモデル部分の役割分担を考えながらクラスを設計する必要があった - ローディング画面のインジケーターの動作と裏側の処理が競合してアニメーションが停止してしまう
→ スレッド機能を用いて非同期処理を行う必要がある
Delphi では描画に関する処理をメインスレッド側で管理する必要があります。なのでインジケーターの動作はメインスレッド側で管理して、そのほかの処理は別スレッドで行いながら最後にメインスレッドに処理内容を同期させました。
- アプリの画面上で画像素材がガビガビしている
→ 解像度の問題だった。マルチプラットフォーム開発では各デバイスの解像度を表す dpi に合わせて1つの素材に対して数種類の大きさ分を用意する必要がある - アプリの画面上で画像素材が小さく見える
→ 拡大/縮小するときにアスペクト比を考慮する必要がある
→ 画像の挿入方法は大きく分けて以下2通りほどあるが、特に後者の場合、画像のアスペクト比を考慮しないと意図したとおりに表示されない
1, そのままオリジナルサイズの状態で挿入する
2, 挿入先のオブジェクトのサイズに合わせて拡大/縮小して挿入する
FMX ではあらかじめ数パターンの素材を用意して、解像度に合った素材を自動選択する機能が用意されています。
- なぜか描画が失敗してアプリが落ちてしまう
→ GPU のメモリ使用が問題だった。大きなサイズかつ大量の画像素材をアニメーションさせたことで GPU のメモリが不足したと考えられる
→ 画像素材を適切なサイズにトリミングさせることで解決した
おわりに
この1年間はアプリ開発、特にクラス設計・UI設計の奥深さと難しさを痛感した1年になりました。
UIデザイン研究のための書籍や Delphi のハンドブックを購読しましたが、まだまだ思うようにいきません。
今年度末も経過を共有できるように頑張ります。
本記事を見て、駆け出しエンジニアの方々が 「アプリ開発っていろいろあるんだなー」「Delphi ってなんだろう。面白そう」と思っていただけたら嬉しいです。
また、Delphi 無料版の Delphi Community Edition がありますので、よろしければお試しください。