Objection, 一個輕量級的Objective-C依賴注入框架
簡介
- 項目主頁: https://github.com/atomicobject/objection
- 實例下載: https://github.com/ios122/ios122 </ul>
- "Annotation" 基于依賴注入.
- 無縫支持自定義集成和依賴擴展.
- 自定義綁定時類的創建方式.
- 元類綁定.
- 協議綁定.
- 實例對象綁定.
- 別名綁定.
- 懶加載.
- 及早計算的單例.
- 自定義初始化方式.
- 自定義參數和默認值. </ul>
- MacOS X 10.8 +
- iOS 7.0 + </ul>
Objection 是一個輕量級的Objective-C依賴注入框架,可同時用于MacOS X 或者iOS.對于那些使用過 Guice (一個Java依賴注入框架)的開發者,會感覺Objection 似曾相識.Objection用來以一種相對容易接受的方式來使你盡可能地不需要管理一個龐大的XML容器或者手動創建對象.
特點
系統要求
使用CocoaPods安裝
注意podfile中需要指明Objection的版本號,否則無法安裝成功.
pod 'Objection', '1.6.1' # 依賴注入.
然后在需要的地方導入即可頭文件即可:
#import <Objection/Objection.h>
使用 Objection
基礎用法
一個類可以使用宏 objection_register (可選)或 objection_register_singleton 注冊到 objection. objection_requires 宏用來聲明objection應該為此類的所有實例提供的依賴. objection_requires 在類的繼承體系中也可以安全使用.
- objection_requires 宏,僅在從從注射器中獲取類的實例時,才有意義.從注射器中獲取類實例的方法,下面會具體討論.
- objection_requires 宏聲明依賴后,使用注射器來獲取此類實例時,會自動創建依賴類的實例,并賦值給響應的屬性.
- 如果使用 objection_register_singleton 宏注冊一個類,并堅持使用注射器來獲取此類的實例,那此類就不用自己實現單例機制了. </ul>
示例.
@class Engine, Brakes;@interface Car : NSObject
// 將會通過依賴注入賦值. @property(nonatomic, strong) Engine engine; // 將會通過依賴注入賦值. @property(nonatomic, strong) Brakes brakes; @property(nonatomic) BOOL awake;
@implementation Car objection_requires(@"engine", @"brakes") @synthesize engine, brakes, awake; @end</pre>
使用選擇器定義依賴.
你也可以使用選擇器來定義依賴.如果給定的選擇器在當前作用域看不見或無法找到,編譯器會產生一個警告.
示例
@implementation Car objection_requires_sel(@selector(engine), @selector(brakes)) @synthesize engine, brakes, awake; @end從Objection中獲取對象.
可以創建一個注射器,然后從這個注射器中獲取指定類或協議的一個實例.注射器各自管理自己的對象上下文.這意味著:Objection中的單例指的是一個注射器中只存在一個某個類的實例,并不一定是真正意義上的單例(即那種應用程序全局唯一的類的實例對象).
- (void)someMethod { JSObjectionInjector *injector = [JSObjection createInjector]; id car = [injector getObject:[Car class]]; }一個給Objection設置一個默認的注射器.這個設置器可以在你的應用或庫內,全局可用.
- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {
JSObjectionInjector *injector = [JSObjection createInjector]; [JSObjection setDefaultInjector:injector]; }
- (void)viewDidLoad {
id myModel = [[JSObjection defaultInjector] getObject:[MyModel class]];
}</pre>
依賴注入
有可能類的實例對象并不是通過注射器創建的,此時如果不做特殊處理,依賴不會被正確處理,相關屬性可能為nil.但是如果對于使用 objection_requires 宏指定依賴的情況,你可以通過 injectDependencies: 方法來實現:即使不使用注射器也能保證依賴被滿足.
@implementation JSTableModel objection_requires(@"RESTClient")
- (void)awakeFromNib {
[[JSObjection defaultInjector] injectDependencies:self];
}
@end</pre>
下標操作
Objection 已經支持使用下標操作來從注射器上下文來獲取對象.
- (void)someMethod { JSObjectionInjector *injector = [JSObjection createInjector]; id car = injector[[Car class]]; }
從Objection中創建的對象.
如果一個對象需要知道它使合適被objection完全初始化的,可以實現方法 awakeFromObjection .注意:對象被Objection完全初始化時會調用awakeFromObjection方法,你在這里可以加入自定義的一些操作;而awake只是一個例子中的自定義標記屬性而已,并不是Objection的一部分.
示例
@implementation Car //... objection_register_singleton(Car)
- (void)awakeFromObjection {
self.awake = YES;
}
@end</pre>
對象工廠
一個對象可以通過對象工廠來從注射器上下文來獲取對象.
自定義JSObjectFactory屬性,需要使用 objection_requires 宏來指明依賴,如 objection_requires(@"objectFactory") .這樣當從注射器中獲取這個類的實例時,會自動獲取與此注射器相關的JSObjectFactory對象工廠實例.
示例
@interface RequestDispatcher @property(nonatomic, strong) JSObjectFactory *objectFactory @end
- (void)awakeFromObjection {
self.awake = YES;
}
@end</pre>
@implementation RequestDispatcher objection_requires(@"objectFactory")
- (void)dispatch:(NSDictionary )params
{
Request request = [self.objectFactory getObject:[Request class]];
request.params = params;
[request send];
}
@end</pre>
模塊
一個模塊就是一組綁定信息.這些綁定信息用來給注射器增加額外的配置信息.它在整合外部依賴和綁定協議到類或實例時特別有用.
實例和協議的綁定
- 綁定一個協議或類到該類型指定的某個實例.
- 綁定一個已經注冊到Objection的類到一個協議.
示例
@interface MyAppModule : JSObjectionModule {
} @end
@implementation MyAppModule
- (void)configure { [self bind:[UIApplication sharedApplication] toClass:[UIApplication class]]; [self bind:[UIApplication sharedApplication].delegate toProtocol:@protocol(UIApplicationDelegate)]; [self bindClass:[MyAPIService class] toProtocol:@protocol(APIService)]; }
@end
- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {
JSObjectionInjector *injector = [JSObjection createInjector:[[MyAppModule alloc] init]]; [JSObjection setDefaultInjector:injector]; }</pre>元類的綁定
有時候,我們僅僅是想使用依賴的某個類的類方法.Objection可以通過協議顯示地支持元類的綁定.這樣就不用再創建一個包裝類來傳遞類方法.要注意的是,它需要定義一個協議來讓Objection知道如何綁定元類到注射器的對象上下文.
示例
@protocol ExternalUtility
- (void)doSomething; //!< 注意此處,確實是
-
減號.通常是不支持讓元類直接支持協議的.此處是以類本身作為對象,來取執行協議,而不是使用該類的某一個實例. @end
- (void)doSomething; //!< 注意此處,確實是
@interface ExternalUtility
- (void)doSomething; @end
@implementation ExternalUtility
- (void)doSomething {...} @end
// Module Configuration
- (void)configure {
[self bindMetaClass:[ExternalUtility class] toProtocol:@protocol(ExternalUtility)];
}
@interface SomeClass { ... } // 使用 'assign' 是因為一個元類不受通常的 retain/release聲明周期限制. // 它將會一直存在,直到應用程序終止(類初始化 -> 應用終止),不管運行時有多少指向它的對象引用. // @property (nonatomic, assign) id<ExternalUtility> externalUtility @end</pre>
提供者
偶爾你可能想要在Objection內部手動構造一個對象.提供者允許你使用自定義的機制來創建某個類型的對象.你可以創建一個 遵守 ObjectionProvider 協議的對象,或者你可以使用一個 block 來創建對象.
如果使用了對像提供者,則原類中的-awakeFromObjection方法在此類的實例通過注射器創建完成后,不會再被調用.
示例
@interface CarProvider : NSObject <JSObjectionProvider> @end@implementation CarProvider
- (id)provide:(JSObjectionInjector )context arguments:(NSArray )arguments { // 手動創建對象的代碼 return car; } @end
@implementation MyAppModule
- (void)configure {
[self bindProvider:[[CarProvider alloc] init] toClass:[Car class]];
[self bindBlock:^(JSObjectionInjector *context) {
} toClass:[Car class]]; } @end</pre>// 手動創建對象. return car;
作用域
一個類被用作模塊作用域內的單例.相反,一個已經注冊的單例在也可以被降級為注射器上下文中一個普通聲明周期的實例對象.
也就是說,你有兩種方式來指定類實例在注射器上下文是單例對象還是普通對象.一種是在類實現中使用 objection_register_singleton 宏,一種是在模塊配置方法中指定作用域為JSObjectionScopeSingleton.
示例
@implementation MyAppModule
- (void)configure {
[self bindClass:[Singleton class] inScope:JSObjectionScopeNormal];
[self bindClass:[Car class] inScope:JSObjectionScopeSingleton];
}
@end</pre>
別名綁定
同一個類或協議的依賴可以使用 objection_requires_names 宏標記,這個宏使用屬性別名字典作為參數.
示例
@interface ShinyCar : NSObject @property (nonatomic, strong) Headlight leftHeadlight; @property (nonatomic, strong) Headlight rightHeadlight; @end @implementation ShinyCar objection_register(ShinyCar) objection_requires_names((@{@"LeftHeadlight":@"leftHeadlight", @"RightHeadlight":@"rightHeadlight"})) @synthesize leftHeadlight, rightHeadlight; @end @implementation NamedModule
- (void)configure
{
[self bind:[[Headlight alloc]init] toClass:[Headlight class] named:@"RightHeadlight"];
[self bindClass:[HIDHeadlight class] toClass:[Headlight class] named:@"LeftHeadlight"];
}
@end</pre>
及早初始化的單例
你可以將已經注冊的單例用作及早初始化的單例.及早初始化的單例,在注射器創建時創建,而不再是懶加載.
注意:如果將一個未注冊為單例的類設置為及早初始化的單例,會引起崩潰.
Example
@implementation MyAppModule
- (void)configure { [self registerEagerSingleton:[Car class]]; }
@end</pre>
從一個已經存在的注射器派生一個新的注射器
一個新的注射器可以使用 withModule: 方法從一個已經存在的注射器創建.這個新的注射器將會和派生它的注射器擁有同樣的綁定信息.
與之相反,如果使用 withoutModuleOfType: ,新注射器就不會包含被標記為不包含的模塊.
示例
injector = [otherInjector withModule:[[Level18Module alloc] init]] withoutModuleOfType:[Level17Module class]];
初始化
默認地,Objection 使用默認的初始化方法init創建對象.如果你想使用其他的初始化方法來初始化對象,可以借助objection_initializer宏.這個宏支持傳遞默認參數(暫時不支持標量參數,即基本類型參數,如數字).
默認參數示例
@implementation ViewController objection_initializer(initWithNibName:bundle:, @"ViewController") @end
自定義參數示例
@implementation ConfigurableCar objection_requires(@"engine", @"brakes") objection_initializer(initWithMake:model:)@synthesize make; @synthesize model;
(instancetype)initWithMake:(NSString )make model:(NSString )model { ... } @end
(void)buildCar { ConfigurableCar *car = [self.objectFactory getObjectWithArgs:[ConfigurableCar class], @"VW", @"Passat", nil]; NSLog(@"Make: %@ Model: %@", car.make, car.model); }</pre>
類方法初始化
@implementation Truck objection_requires(@"engine", @"brakes") objection_initializer(truckWithMake:model:)
- (instancetype)truckWithMake:(NSString ) make model: (NSString )model {
...
}
@end</pre>
專用初始化方法
@implementation ConfigurableCar
- (instancetype) initWithModel:(NSString *)model { //.... } @end
- (void)buildCar {
ConfigurableCar *car = [self.objectFactory getObject:[ConfigurableCar class],
}</pre>initializer: @selector(initWithModel:) withArgumentList:@[@"Passat"]];
測試
如果你正在使用 Kiwi 來進行應用的測試, 請檢出 MSSpec .它提供了一種把虛擬數據注入到你的測試標準中的便利方式.
原文 http://www.cnblogs.com/ios122/p/4947805.html