『iOS 知識体系の全貌を体験する』、今日はアプリ開発編(下)を続けます。
この章では、iOS 開発における JSON 解析、レイアウトフレームワーク、リッチテキスト、TDD/BDD、コーディング規範 について説明します。
26 | JSON 解析#
背景:異なるプログラミング言語間でデータ通信を行う際、通信データの規範はどのように決定されるべきか?そのためには、汎用的であり、各プログラミング言語がサポートするデータ形式が必要です。
次に登場するのは JSON です。
JSON の正式名称は JavaScript Object Notation であり、最初は JavaScript 言語のサブセットとして設計されましたが、最終的にはプログラミング言語に依存しないため、JSON は一般的なオープンスタンダードのデータ形式となりました。
現在、多くのプログラミング言語が JSON の生成と解析をサポートしているため、JSON のようなデータ形式は背景で述べたニーズを満たしています。
JSON は二つの構造に基づいています:
-
キーと値のペアの集合:具体的な実装には辞書、ハッシュテーブル、オブジェクト、構造体などがあり、例:
{"key1": "val1","key2": "val2"}
。 -
順序付き値のリスト:具体的な実装には配列、ベクトル、リストなどがあり、例:
[1,3,5]
。
JSON のその他の特徴:
-
ネストをサポート:上記の値は文字列、数字、オブジェクト、配列、ブール値、空値のいずれかであることができます;
-
コメントをサポートしない;
-
水平タブ、改行、キャリッジリターンはすべてスペースとして扱われます。
ほとんどの言語のロジックは構文木構造に変換でき、構文木は JSON で記述できます。したがって、JSON の使用シーンは多くあります:
-
ビジネスデータを記述し、ビジネスデータを動的に更新できるようにする;
-
ビジネスロジックを記述し、ビジネスロジックの動的化を実現する;
-
ページレイアウトを記述する。
どのように解析するか?
Apple は、JSON を解析するために NSJSONSerialization(OC)/ JSONSerialization(Swift)クラスを提供しています。関連するサードパーティフレームワークには JSONModel、Mantle、MJExtension、YYModel があり、これらはすべてネイティブクラスのラッパーです。
より高い解析性能を追求する場合は、simdjson(2019 年 2 月にリリース)を検討してください。これは、毎秒ギガバイトの JSON ファイルを解析できるとされています。バイト単位で上から下への再帰解析を行う従来の JSON 解析方法に比べて、最適化の考え方は解析の並列化にあります。参考:
-
毎秒ギガバイトの JSON を解析する—— 論文
27 | 自動レイアウトと比較して、Flexbox はどこが優れているのか?#
Flexbox の “地位”:は、著名なレイアウトライブラリである React Native、Weex、Texture(AsyncDisplayKit)が採用しているレイアウトの考え方であり、同時に Apple の公式クラス UIStackView が採用しているレイアウトの考え方でもあります。
React Native と Weex の Flexbox アルゴリズムの実装は、Yogaという C++ ライブラリによって行われています。
以下の図のように、Flexbox アルゴリズムの主要な考え方は、flex container(コンテナ)がその flex item(アイテム)の幅、高さ、順序を変更できるようにすることです。例えば、アイテムを拡大 / 縮小して利用可能なスペースに適合させる(埋める / 超過を防ぐ)ことができます。
Flexbox についての詳細な説明は、以下を参考にできます:
-
Flex レイアウトチュートリアル:文法編—— 阮一峰
-
Flex レイアウトチュートリアル:実例編—— 阮一峰
-
Flex レイアウトの例——Blog
では、自動レイアウトと比較して、Flexbox はどこが優れているのか?
-
提供されるレイアウト方法がより便利で、より包括的で、より規範的です;
-
レスポンシブで、クロスプラットフォーム性が良い:現在すべてのブラウザがサポートしており、iOS と Android でもサポートされています。
レスポンシブとは、対象を直接操作するのではなく、代理を通じて操作の目的を達成することを指します。
28 | さまざまなリッチテキストの表現要求にどう対処するか?#
リッチテキストとは?それは属性を持つ文字列のことです。
-
異なるフォント、異なるサイズ、異なる背景、異なる色、異なる文字間隔の文字を含むことができます;
-
段落や画像とテキストの混合などの属性も設定できます。
では、リッチテキストをどのように表示するか?ここでは 2 つの状況に分けます:
1)HTML で記述されたリッチテキスト
この方法で記述されたリッチテキストは、WKWebView コントロールの loadHTMLString:baseURL:
メソッドを使用して表示できます。
さらに、HTML 内の画像リソースはネットワークリクエストを通じて取得する必要があるため、リクエスト回数を減らすためにキャッシュ戦略を考慮することができます。
2)ネイティブ iOS コードで記述されたリッチテキスト
長いリストのシナリオ(複数のデータを一度に返してフロントエンドでレンダリングする)では、一般的にパフォーマンスの要求が高いため、この方法で記述されたリッチテキストが使用されます。
この方法で記述されたリッチテキストは、Apple の公式のTextKitやサードパーティのYYTextを使用して表示できます。
その中で、YYText には多くの特長があります:
-
非同期テキストレイアウトとレンダリングのパフォーマンスが非常に良い;
-
UILabel と UITextView と互換性がある;
-
カスタムの NSMutableAttributedString カテゴリは、基本クラスを簡素化するだけでなく、UIView、CALayer などの埋め込み機能も追加しています。
小結:
-
HTML で記述されたリッチテキストはより読みやすく、より維持しやすい;
-
ネイティブコードで記述されたリッチテキストはパフォーマンスが高い。
したがって、両者の利点を組み合わせたい場合は、HTML でリッチテキストを記述し、表示前に HTML をネイティブコードに変換することができます(著者のHTNプロジェクトを参照してください。HTML コードをネイティブコードに変換する機能を実現しています)。
つまり、リッチテキスト → HTML → ネイティブコード → 表示です。
29 | TDD と BDD をどのように行うか?#
背景:影響範囲が広いコードを書くとき、検証が必要な場所が非常に多くなり、それに応じて手動検査の時間コストも非常に高くなります。
では、コードを書いた後の検証効率をどのように向上させることができるでしょうか?
答えは開発とテストを同時に行い、問題を早期に発見することです。
テスト範囲に基づいて、テストは以下のように分けられます:
-
ユニットテスト(開発者が担当)
-
統合テスト(テストチームが担当)
-
システムテスト(テストチームが担当)
その中で、ユニットテストはモジュールテストとも呼ばれ、このユニットはクラスのメソッドである場合もあれば、モジュールの特定の関数である場合もあります。また、開発者は各ユニットの責任を明確に保つように注意する必要があります。
開発モデルに基づいて、開発方式は以下のように分けられます:
-
TDD(テスト駆動開発)
-
BDD(振る舞い駆動開発)
TDDの開発思考は:
-
まずテストケースを書く;
-
コードの最適化を考慮せずに機能実装コードを迅速に書く;
-
機能開発が完了したら、テストケースの保証の下でコードをリファクタリングし、コード品質を向上させる。
そのテストケースは主に開発中の最小単位を対象としており、ユニットテストに適しています。
考え方として、TDD と機能要件を受け取った後に直接機能を開発することの違いは:
-
まず機能をテストする方法を考え、その後コードを書く方法を考えることで、コードの最適化により多くの時間と空間を提供します;
-
数バージョン後に最適化を行っても、以前に書いたテストケースを通過できれば、コード品質を保証できます。
PS:ちょっと「初心を忘れない」感じがありますね~
BDDは TDD の進化です。これにより:
-
行動に基づいて機能テストを行い、DSL(ドメイン特化言語)を使用してテストケースを記述します;
-
テストケースは文書のように見え、より読みやすく、より維持しやすいです。
そのテストケースは行動の記述であり、テスト範囲はより広く、統合テストやシステムテストに適しています。
また、BDD が使用する DSL 言語(規範、標準、高い可読性)のおかげで、開発者は BDD を使用して効率的に問題を発見できるだけでなく、テストチームも参加して記述することが容易になります。
BDD の OC フレームワークには Kiwi、Specta、Expecta などがあり、Swift フレームワークには Quick があります。
著者はKiwiフレームワークを推奨します。なぜなら、Specta の DSL モード、Expecta フレームワークの期待構文、モック、スタブ機能が含まれているからです。具体的な使用法はKiwi Wiki(Specs | Expectations | Mocks and Stubs | 非同期テスト)を参照してください。
モック:モックオブジェクト、例えば Null オブジェクトのモック、クラスのインスタンスのモック、プロトコルのインスタンスのモック。
スタブ:スタブは、セレクタやメッセージパターンが固定の結果を返すようにし、実際のオブジェクトとモックオブジェクトの両方をサポートします。
小結:
-
TDD も BDD も、まずテストケースを書く(さまざまな異常条件や入力出力の境界を考慮する必要があります)、その後コードを開発します;
-
良いモジュール化アーキテクチャと TDD、BDD は相互に促進し合います。
著者の提案:基礎能力の機能開発には TDD と BDD を優先して使用し、基礎能力の安定を保証した後、時間が許す限りコアビジネスを考慮します。
30 | 適切なコーディング規範をどのように策定するか?#
背景:チームが統一されたコーディング規範を持つことで、チームメンバー間の相互認識感の欠如の問題(コードスタイルの不一致による)をより効果的に回避できます。
では、良いコード規範とは何か、どのような側面を考慮する必要があるのでしょうか?
まず、いくつかの優れた企業のコード規範を参照できます:
-
Apple: Apple Developer Documentation | Technologies(インターフェース設計)、Cocoa のコーディングガイドライン
-
Google: Google Objective-C Style Guide 中文版、Google Swift Style Guide 中文版
-
その他: Spotify Objective-C Coding Style、NYTimes Objective-C Style Guide、Raywenderlich Objective-C Style Guide、Raywenderlich Swift Style Guide。
次に、簡単な参考を示します:
-
定数:マクロ定義ではなく、型定数を使用します。
-
変数:機能を明確に示し、できれば型をサフィックスとして追加します;グローバル変数を使って値を渡すのではなく、パラメータを通じて値を渡します(機能モジュール間の結合を減らします)。
-
プロパティ:OC では、できるだけ get メソッドを通じて遅延ロードを行います(無駄なメモリ使用と余分な計算を避けるため);Swift では、プロパティが読み取り専用の場合、get 句を省略できます。
-
条件文:デフォルト処理を減らすか使用しない、特に Switch で列挙型を処理する場合(Swift で Switch 文を書くとき、default ブランチを追加しないと、列挙型に新しい値が追加されたときにコンパイラがブランチ処理を追加するように警告します);ネスト処理を減らす(可読性を向上させるため)、Swift では guard 構文を十分に活用できます。
-
ループ文:continue と break の使用を減らす(可読性を向上させるため)、Swift では guard を統一して使用できます。
-
関数:関数名は目的を示す;各関数は最小単位のロジックを処理し、単一責任原則を満たす;関数内でグローバル変数を使ってデータを渡すことをできるだけ避ける(結合を減らし、ユニットテストの正確性を向上させる);関数の引数をチェックすることに注意する(堅牢性を向上させる)、Swift の guard 構文も引数のチェックに適用できます。
-
クラス:OC では、クラスのヘッダーファイルに他のクラスのヘッダーファイルをできるだけ少なくインポートし、
@class
で宣言し、実装ファイルで必要なヘッダーファイルを#import
でインポートします(@class
を使用してコードのコンパイルを保証し、#import
を使用してコードの実行を保証します。参考:OC における @class と #import の違い——CSDN);継承やプロトコルの遵守の状況では、他のクラスのヘッダーファイルをインポートせざるを得ないため、コード設計時には継承をできるだけ減らします(継承関係が多すぎるとコードの保守や修正が不利になります。例えば、親クラスを修正する際にはすべての子クラスへの影響を考慮する必要があります)。 -
カテゴリ:カテゴリに追加するメソッド名にはできるだけプレフィックスを付け、システム提供のクラスのカテゴリの場合は必ずプレフィックスを付けます(メソッド名の重複問題を避けるため);1 つのクラスの公開メソッドを異なるカテゴリに分類して管理・保守しやすくします(特に複数人がそれぞれ異なる機能コードを保守するシナリオに適しています)。
💡:
-
コードロジックが明確であることは、高品質なコードの最も基本的かつ必要な条件です。コードが明確でない場合、他の拡張、再利用、簡潔さ、優雅さは論外です。
-
コードを書く際の最初のタスクは他の人が理解できるようにすることであり、過度のエンジニアリング(特定のビジネスに対するエンジニアリング設計)を避けることです。
-
新しい言語機能やブラックマジックの使用を減らし、使用する場合は多くのコメントを追加します。
最後に、どのようにしてコード規範を実行に移すか?
最良の方法は コードレビュー(Code Review)です。コードレビューを通じて、あなたは:
-
コード規範がチームメンバーによって実行されているかどうかを確認できます;
-
コードの書き方が不規則なメンバーをタイムリーに指導できます。
コードレビューは二段階に分けられます:
-
次に、手動チェックを行います(レビュアーはいいねやコメントを付けることができます)。これにより、コード規範を実行に移す効果を得るだけでなく、メンバー間のコミュニケーションや相互学習を促進することもできます。
さて、アプリ開発編はもうすぐ終わりです(次は iOS 開発学習資料の紹介が残っています)。次回からは原理編の内容に入ります。iOS システムカーネル XNU や iOS のブラックマジックの背後にある原理など……(🤫少し想像の余地を残しておきます)。
Bo2SS はそれらを明確に説明するために全力を尽くしますので、皆さんのご支援をよろしくお願いします~次回お会いしましょう!