シリーズの最後の部分では、iOSのDependency Injectionフレームワーク-Typhoonに会い、Ramblerプロジェクトでの基本的な使用例を見てみました。 今回は、その内部構造の研究を掘り下げます。
サイクル「iOSアプリケーションの依存関係を正しく管理する」
- 台風の紹介
- 台風装置
- モジュラリティ台風
- 台風のヒントとコツ
- 台風の代替
- (オプション)Rambler.iOS#3。 iOSでの依存性注入。 スライド
- (オプション)Rambler.iOS#3。 iOSでの依存性注入。 映像
はじめに
まず、この記事で積極的に使用される用語の小さな用語集を分析します。
- アセンブリ ( [essembley]と読みます)。 最も近いロシアの同等物は、アセンブリ、建設です。 Typhoonでは、これらはすべてのアプリケーション依存関係の構成を含むオブジェクトであり、これは本質的にアーキテクチャ全体のバックボーンです。 活性化されている外の世界にとって、彼らは普通の工場のように振る舞います。
- 定義 ロシア語への翻訳に関しては、元のバージョンに最も近いバージョンである構成に最も感銘を受けました。 TyphoonDefinition-これらは一種の依存モデルであり、作成されたオブジェクトのクラス、そのプロパティ、ライフサイクルのタイプなどの情報を含むオブジェクトです。 前の記事のほとんどの例は、同じTyphoonDefinition構成オプションに関するものでした。
- 範囲 。 ここではすべてが簡単です-これは、Typhoonを使用して作成されたオブジェクトのライフサイクルのタイプです。
- アクティベーション 。 すべてのTyphoonAssembyの下位オブジェクトがTyphoonDefinitionではなく実際のクラスインスタンスを提供し始めるプロセス 。 活性化の本質と原理については、以下で説明します。
そしてもう一度、フレームワークの基本原則を理解することが非常に重要であることを強調します。その後、実装の詳細にとどまることなく、他のすべての台風バンを静かに進め、研究することができます。
膨大なコードリストで記事を煩雑にしないために、特定のフレームワークファイルを定期的に参照し、最も興味深い点のみを引用します。 執筆時点でのTyphoon Frameworkの現在のバージョンは3.1.7であることに注意してください。
初期化
Typhoonを使用したアプリケーションのライフサイクルは次のとおりです。
- main.mを呼び出す
- UIApplicationの作成- [UIApplication init]
- UIAppDelegateの作成- [UIAppDelegate init]
- setDelegateメソッドを呼び出します。作成されたUIApplicationインスタンスで
- ブリッジクラスTyphoonStartupのsetDelegate実装を呼び出す:
- UIAppDelegateインスタンスで-applicationDidFinishLaunching:withOptions:メソッドを呼び出す
ブリッジングされたsetDelegateで、開始アセンブリの作成とアクティブ化が行われます。
工場の自動読み込みは2つの場合に可能です :キーTyphoonInitialAssembliesの下でInfo.plistでそれらのクラスを指定しました:
+(id)factoryFromPlistInBundle:(NSBundle *)バンドル
+ (id)factoryFromPlistInBundle:(NSBundle *)bundle { TyphoonComponentFactory *result = nil; NSArray *assemblyNames = [self plistAssemblyNames:bundle]; NSAssert(!assemblyNames || [assemblyNames isKindOfClass:[NSArray class]], @"Value for 'TyphoonInitialAssemblies' key must be array"); if ([assemblyNames count] > 0) { NSMutableArray *assemblies = [[NSMutableArray alloc] initWithCapacity:[assemblyNames count]]; for (NSString *assemblyName in assemblyNames) { Class cls = TyphoonClassFromString(assemblyName); if (!cls) { [NSException raise:NSInvalidArgumentException format:@"Can't resolve assembly for name %@", assemblyName]; } [assemblies addObject:[cls assembly]]; } result = [TyphoonBlockComponentFactory factoryWithAssemblies:assemblies]; } return result; }
AppDelegateで-initialFactoryメソッドを実装しました:
+(TyphoonComponentFactory *)factoryFromAppDelegate:(id)appDelegate
+ (TyphoonComponentFactory *)factoryFromAppDelegate:(id)appDelegate { TyphoonComponentFactory *result = nil; if ([appDelegate respondsToSelector:@selector(initialFactory)]) { result = [appDelegate initialFactory]; } return result; }
どちらも実行されていない場合は、コード内の他の場所で手動でアセンブリを作成する必要がありますが、これは推奨されません。
Typhoonの初期化の詳細については、次のソースファイルをご覧ください。
- TyphoonStartup.m
- TyphoonComponentFactory.m
アクティベーション
このプロセスは、フレームワークの作業の鍵です。 アクティベーションとは、 TyphoonBlockComponentFactoryクラスのオブジェクトの作成を指し、そのインスタンスは、アクティベートされたすべてのアセンブリの内部にあります。 したがって、すべてのアセンブリは、実際の工場と通信するためのインターフェースの役割を果たします。
あまり詳細に説明することなく何が起こるか見てみましょう
- TyphoonBlockComponentFactoryで、 -initWithAssemblies:イニシャライザーが呼び出され、入力にアセンブリー配列が渡されます。これはアクティブ化する必要があります。
- アクティブ化されたアセンブリによって作成された各定義には、独自の一意のキー(ランダムな文字列+メソッド名)が割り当てられます。
- すべてのTyphoonDefinitionは、新しく作成されたTyphoonBlockComponentFactoryのレジストリ配列に追加されます 。
もちろん、問題はこれらの3つのポイントに限定されません:登録されたTyphoonDefinitionごとに 、 アスペクトが追加され、すべての依存関係のゲッターがブリッジされ、それによってオブジェクトグラフの初期化チェーンが作成され、インスタンスプールがTyphoonBlockComponentFactoryで作成されます -一般に、フレームワークが動作するように多数の異なるアクションが実行されます。 この記事では、Typhoonの一般原則の理解を妨げる可能性があるため、検討中の各手順の詳細には触れません。
TyphoonAssemblyがどのようにアクティブ化されるかを調査しました 。なぜこれを行うべきなのかを理解する必要があります。 TyphoonDefinitionに依存関係を与えるメソッドにアセンブリを手動でジャークするたびに、次のことが起こります。
-(void)forwardInvocation:(NSInvocation *)anInvocation
- (void)forwardInvocation:(NSInvocation *)anInvocation { if (_factory) { [_factory forwardInvocation:anInvocation]; } ... }
_factoryでは、結果のNSInvocationが処理され、次のメソッドの呼び出しに変換されます。
-(id)componentForKey:(NSString *)キー引数:(TyphoonRuntimeArguments *)引数
- (id)componentForKey:(NSString *)key args:(TyphoonRuntimeArguments *)args { if (!key) { return nil; } [self loadIfNeeded]; TyphoonDefinition *definition = [self definitionForKey:key]; if (!definition) { [NSException raise:NSInvalidArgumentException format:@"No component matching id '%@'.", key]; } return [self newOrScopeCachedInstanceForDefinition:definition args:args]; }
セレクターメソッドから生成されたキーは、 TyphoonBlockComponentFactoryに登録されている定義の1つを取得し、それに基づいて、新しいインスタンスが作成されるか、キャッシュされたインスタンスが再利用されます。
アクティベーション手順の詳細については、次のソースファイルをご覧ください。
- TyphoonAssembly.m
- TyphoonBlockComponentFactory.m
- TyphoonTypeDescriptor.m
- TyphoonAssemblyDefinitionBuilder.m
- TyphoonStackElement.m
ストーリーボードを使用する
内部では、TyphoonはUIStoryboardのサブクラスTyphoonStoryboardを使用します。 あなたの目を引く最初の機能はファクトリーメソッドです。これは、追加のパラメーター-ファクトリーによって親と異なります:
+ (TyphoonStoryboard *)storyboardWithName:(NSString *)name factory:(id<TyphoonComponentFactory>)factory bundle:(NSBundle *)bundleOrNil;
現在のストーリーボードの画面の定義が検索されるのは、 TyphoonComponentFactoryプロトコルを実装するこのファクトリー内です。 ViewControllersの依存性注入のすべての段階を見てみましょう。
- まず、 TyphoonStoryboardでオーバーライドされた-instantiateViewControllerWithIdentifier:メソッドに到達します。
- 目的のコントローラーのインスタンスは、super'aを呼び出して作成されます。
- 現在のコントローラーとその子コントローラーのすべての依存関係の注入が開始されます。
-(void)injectPropertiesForViewController:(UIViewController *)viewController- (void)injectPropertiesForViewController:(UIViewController *)viewController { if (viewController.typhoonKey.length > 0) { [self.factory inject:viewController withSelector:NSSelectorFromString(viewController.typhoonKey)]; } else { [self.factory inject:viewController]; } for (UIViewController *controller in viewController.childViewControllers) { [self injectPropertiesForViewController:controller]; } }
- TyphoonBlockComponentFactoryで既によく知られている手順が発生します。現在のTyphoonDefinitionクラスを検索し、依存関係グラフを挿入するプロセスを開始します。
ここでは、アプリケーションでTyphoonStoryboardを操作する特定の実装については説明しません。このトピックについては、以下の記事のいずれかで説明します。
次のソースファイルで、ストーリーボードの操作の実装の詳細を確認できます。
- TyphoonStoryboard.m
- TyphoonBlockComponentFactory.m
台風定義
何らかの形で引用したほとんどすべてのスニペットで、 TyphoonDefinitionクラスが見つかります。 用語を列挙するときに述べたように、 TyphoonDefinitionは作成された依存関係の一種の構成クラスです。したがって、そのインターフェイスは私たちにとって最も重要です。
- Class _type-作成された依存関係のクラス
- NSString * _key -Typhoonがアクティブ化されたときに生成される一意のキー、
- TyphoonMethod * _initializer- 初期化子の注入中に作成され、目的の初期化子のシグネチャとそのパラメータのコレクションを含むオブジェクト、
- TyphoonMethod * _beforeInjections-依存性注入が実行される前に呼び出されるメソッド、
- TyphoonMethod * _afterInjections-依存関係の注入後に呼び出されるメソッド、
- NSMutableSet * _injectedProperties- プロパティインジェクションによってインストールされた依存関係のコレクション、
- NSMutableOrderedSet * _injectedMethods-特定の依存関係を渡すメソッドのコレクション( メソッドインジェクション )、
- TyphoonScopeスコープ -作成されたオブジェクトのライフサイクルのタイプ、
- TyphoonDefinition * _parent-ベースTyphoonDefinition 、そのすべてのプロパティは現在のプロパティに継承されます。
- BOOL abstractは、現在の構成は継承の実装にのみ使用でき、それが表すオブジェクトは直接作成しないことを示すフラグです。
上記のすべてのプロパティのうち、オブジェクトのスコープには特別な注意が必要です。
TyphoonDefinitionの動作の詳細については、次のソースファイルで確認できます。
- TyphoonDefinition.m
- TyphoonAssemblyDefinitionBuilder.m
- TyphoonFactoryDefinition.m
- TyphoonInjectionByReference.m
- TyphoonMethod.m
台風スコープ
オブジェクトのライフサイクルのさまざまなタイプについて言えば、 使用されるTyphoonBlockComponentFactoryインスタンスの存続期間に依然として固く結びついていることを明確に理解することが重要です-このファクトリーがメモリから解放されると、オブジェクトのすべてのグラフが解放されます。
TyphoonScopeの各値が何につながるかを見てみましょう。
- TyphoonScopeObjectGraph
依存関係グラフを作成するプロセスでは、このスコープを持つオブジェクトは一度だけ作成されます。 技術的には、次のようになります:インスタンス(任意の定義)を作成する前に、オブジェクトグラフスコープオブジェクトのプールが作成され、依存関係グラフの構築プロセスで、すべてのオブジェクトグラフスコープがこのプールに移動し、インスタンスが作成された後、このプール片付けた。 - TyphoonScopePrototype
このスコープでTyphoonDefinitionにアクセスするたびに、クラスの新しいインスタンスが作成されます。 - TyphoonScopeSingleton
このようなライフサイクルを持つオブジェクトは、 TyphoonComponentFactoryの存続期間を通じて存続します。 - TyphoonScopeLazySingleton
名前が示すように、これは最初にアクセスしたときに作成されるシングルトンです。 - TyphoonScopeWeakSingleton
このようなTyphoonDefinitionを使用して作成されたシングルトンは、少なくとも1つのオブジェクトが参照している限りメモリ内にあります。そうでない場合は解放されます。
作成されたオブジェクトは、その構成のscopeプロパティに応じて、 TyphoonComponentFactoryプールの1つに格納され、それぞれが特定の方法で機能します。
Typhoon依存関係キャッシュの動作原理の詳細については、次のソースをご覧ください。
- TyphoonComponentFactory.m
- TyphoonWeakComponentPool.m
- TyphoonCallStack.m
おわりに
Typhoon Frameworkの最も基本的な原則-初期化、工場アクティベーション、 TyphoonAssemblyデバイス、 TyphoonStoryboard 、 TyphoonDefinitionおよびTyphoonBlockComponentFactory 、作成されたオブジェクトのライフサイクル機能のみを考慮しました。 ライブラリには多くの興味深い概念が含まれており、その実装は単に魅力的な場合もあります。
数日かけて頭で勉強することを強くお勧めします-これは、 「無料でSMSを使わずにSwiftでマウスを使ってXcodeで作業する」スタイルと「 ファイルアップロードインジケーターの高度なアニメーション」スタイルで多数のレッスンを学ぶよりも価値のある選択肢です。
シリーズの次のパートでは、1つの巨大な工場の出現を回避し、アセンブリレベルのアーキテクチャをモジュールに正しく分割し、テスト全体をカバーする方法を学習します。
サイクル「iOSアプリケーションの依存関係を正しく管理する」
- 台風の紹介
- 台風装置
- モジュラリティ台風
- 台風のヒントとコツ
- 台風の代替
- (オプション)Rambler.iOS#3。 iOSでの依存性注入。 スライド
- (オプション)Rambler.iOS#3。 iOSでの依存性注入。 映像