『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 は NSJSONSerialization (OC) / JSONSerialization (Swift) クラスを提供して JSON を解析します。関連するサードパーティのフレームワークには 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 スタイルガイド 日本語版、Google Swift スタイルガイド 日本語版
-
その他: Spotify Objective-C コーディングスタイル、NYTimes Objective-C スタイルガイド、Raywenderlich Objective-C スタイルガイド、Raywenderlich Swift スタイルガイド。
次に、簡単な 参考 を示します:
-
定数:マクロ定義ではなく、型定数を使用します。
-
変数:機能を明確に反映し、できれば型をサフィックスとして追加します;グローバル変数を使用して値を渡すのではなく、パラメータを通じて値を渡します(機能モジュール間の結合を減らす)。
-
プロパティ:OC では、できるだけ get メソッドを使用して遅延ロードを行います(無駄なメモリ占有と余分な計算を避けるため);Swift では、プロパティが読み取り専用の場合は、その get 節を省略できます。
-
条件文:デフォルト処理を減らすか使用しない(特に列挙型を処理する際の Switch 使用時に注意);ネスト処理を減らす(可読性を向上させるため)、Swift では guard 構文を十分に活用できます。
-
ループ文:continue と break の使用を減らす(可読性を向上させるため)、Swift では guard を統一して使用できます。
-
関数:関数名は目的を反映する;各関数は最小単位のロジックを処理し、単一責任原則を満たす;関数内でグローバル変数を使用してデータを渡すことをできるだけ避ける(結合を減らし、単体テストの正確性を向上させる);関数の引数を確認することに注意(堅牢性を向上させる)、Swift の guard 構文も引数の確認に適用できます。
-
クラス:OC では、クラスのヘッダーファイルに他のクラスのヘッダーファイルをできるだけ少なくインポートし、
@class
を使用して宣言し、実装ファイルで必要なヘッダーファイルを#import
でインポートします(@class
を使用してコードのコンパイルを保証し、#import
を使用してコードの実行を保証します。参考:OC における @class と #import の違い——CSDN);継承やプロトコルの遵守の状況では、他のクラスのヘッダーファイルをインポートせざるを得ないため、コード設計時には継承をできるだけ減らします(継承関係が多すぎるとコードの保守や修正に不利です。たとえば、親クラスを修正する際にすべての子クラスへの影響を考慮する必要があります)。 -
カテゴリ:カテゴリに追加するメソッド名にはできるだけプレフィックスを付け、システム提供クラスのカテゴリの場合は必ずプレフィックスを付けます(メソッド名の重複問題を避けるため);1 つのクラスの公有メソッドを異なるカテゴリに分類し、管理と保守を容易にします(特に複数の人がそれぞれ異なる機能コードを保守するシーンに適しています)。
💡:
-
コードロジックが明確であることは、高品質なコードの最も基本的かつ必要な条件です。コードが明確でない場合、他の拡張、再利用、簡潔さ、優雅さは論外です。
-
コードを書く際の最も重要なタスクは、他の人が理解できるようにすることであり、過度のエンジニアリング(特定のビジネスに対するエンジニアリング設計)を避けることです。
-
新しい言語機能やブラックマジックの使用を減らし、使用する場合は多くのコメントを追加します。
最後に、コーディング規範を 実行に移す 方法は?
最良の方法は コードレビュー です。コードレビューを通じて、あなたは:
-
コード規範がチームメンバーによって実行されているかどうかを確認できます;
-
同時に、コードの書き方が不規則なメンバーに対して適時に指導できます。
コードレビューは 二段階 に分けられます:
-
次に、手動チェック(レビュー担当者はいいねやコメントを付けることができます)を行います。これにより、コード規範を実行に移す効果が得られるだけでなく、メンバー間のコミュニケーションと相互学習を促進できます。
さて、アプリ開発編はもうすぐ終わります(次回は iOS 開発学習資料 の紹介が残っています)。次回は 原理編 の内容に入ります。iOS システムカーネル XNU、iOS のブラックマジックの背後にある原理などに関わります……(🤫少し想像の余地を残しておきます)。
Bo2SS はそれらを明確に説明するために全力を尽くしますので、皆さんのご支援をよろしくお願いします~次回お会いしましょう!