iOSアプリで依存関係を正しく管理する:Typhoonデバイス





シリーズの最後の部分では、iOSのDependency Injectionフレームワーク-Typhoonに会い、Ramblerプロジェクトでの基本的な使用例を見てみました。 今回は、その内部構造の研究を掘り下げます。



サイクル「iOSアプリケーションの依存関係を正しく管理する」





はじめに



まず、この記事で積極的に使用される用語の小さな用語集を分析します。



そしてもう一度、フレームワークの基本原則を理解することが非常に重要であることを強調します。その後、実装の詳細にとどまることなく、他のすべての台風バンを静かに進め、研究することができます。



膨大なコードリストで記事を煩雑にしないために、特定のフレームワークファイルを定期的に参照し、最も興味深い点のみを引用します。 執筆時点でのTyphoon Frameworkの現在のバージョンは3.1.7であることに注意してください。



初期化



Typhoonを使用したアプリケーションのライフサイクルは次のとおりです。







ブリッジングされた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の初期化の詳細については、次のソースファイルをご覧ください。



アクティベーション



このプロセスは、フレームワークの作業の鍵です。 アクティベーションとは、 TyphoonBlockComponentFactoryクラスのオブジェクトの作成を指し、そのインスタンスは、アクティベートされたすべてのアセンブリの内部にあります。 したがって、すべてのアセンブリは、実際の工場と通信するためのインターフェースの役割を果たします。



あまり詳細に説明することなく何が起こるか見てみましょう

  1. TyphoonBlockComponentFactoryで、 -initWithAssemblies:イニシャライザー呼び出され、入力にアセンブリー配列が渡されます。これはアクティブ化する必要があります。
  2. アクティブ化されたアセンブリによって作成された各定義には、独自の一意のキー(ランダムな文字列+メソッド名)が割り当てられます。
  3. すべての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つを取得し、それに基づいて、新しいインスタンスが作成されるか、キャッシュされたインスタンスが再利用されます。



アクティベーション手順の詳細については、次のソースファイルをご覧ください。



ストーリーボードを使用する



内部では、TyphoonはUIStoryboardのサブクラスTyphoonStoryboardを使用します。 あなたの目を引く最初の機能はファクトリーメソッドです。これは、追加のパラメーター-ファクトリーによって親と異なります:

 + (TyphoonStoryboard *)storyboardWithName:(NSString *)name factory:(id<TyphoonComponentFactory>)factory bundle:(NSBundle *)bundleOrNil;
      
      





現在のストーリーボードの画面の定義が検索されるのは、 TyphoonComponentFactoryプロトコルを実装するこのファクトリーです。 ViewControllersの依存性注入のすべての段階を見てみましょう。

  1. まず、 TyphoonStoryboardでオーバーライドされた-instantiateViewControllerWithIdentifier:メソッドに到達します。
  2. 目的のコントローラーのインスタンスは、super'aを呼び出して作成されます。
  3. 現在のコントローラーとその子コントローラーのすべての依存関係の注入が開始されます。

    -(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]; } }
          
          



  4. TyphoonBlockComponentFactoryで既によく知られている手順が発生します。現在のTyphoonDefinitionクラスを検索し、依存関係グラフを挿入するプロセスを開始します。



ここでは、アプリケーションでTyphoonStoryboard操作する特定の実装については説明しません。このトピックについては、以下の記事のいずれかで説明します。



次のソースファイルで、ストーリーボードの操作の実装の詳細を確認できます。



台風定義



何らかの形で引用したほとんどすべてのスニペットで、 TyphoonDefinitionクラスが見つかります。 用語を列挙するときに述べたように、 TyphoonDefinitionは作成された依存関係の一種の構成クラスです。したがって、そのインターフェイスは私たちにとって最も重要です。



上記のすべてのプロパティのうちオブジェクトのスコープには特別な注意が必要です。



TyphoonDefinitionの動作の詳細については、次のソースファイルで確認できます。



台風スコープ



オブジェクトのライフサイクルのさまざまなタイプについて言えば、 使用されるTyphoonBlockComponentFactoryインスタンスの存続期間に依然として固く結びついていることを明確に理解することが重要です-このファクトリーがメモリから解放されると、オブジェクトのすべてのグラフが解放されます。

TyphoonScopeの各値何につながるかを見てみましょう。



作成されたオブジェクトは、その構成のscopeプロパティに応じて、 TyphoonComponentFactoryプールの1つに格納され、それぞれが特定の方法で機能します。



Typhoon依存関係キャッシュの動作原理の詳細については、次のソースをご覧ください。



おわりに



Typhoon Frameworkの最も基本的な原則-初期化、工場アクティベーション、 TyphoonAssemblyデバイス、 TyphoonStoryboardTyphoonDefinitionおよびTyphoonBlockComponentFactory 、作成されたオブジェクトのライフサイクル機能のみを考慮しました。 ライブラリには多くの興味深い概念が含まれており、その実装は単に魅力的な場合もあります。



数日かけて頭で勉強することを強くお勧めします-これは、 「無料でSMSを使わずにSwiftでマウスを使ってXcodeで作業する」スタイルと ファイルアップロードインジケーターの高度なアニメーション」スタイルで多数のレッスンを学ぶよりも価値のある選択肢です。



シリーズの次のパートでは、1つの巨大な工場の出現を回避し、アセンブリレベルのアーキテクチャをモジュールに正しく分割し、テスト全体をカバーする方法を学習します。



サイクル「iOSアプリケーションの依存関係を正しく管理する」





便利なリンク






All Articles