开源库信息
objection 是一个iOS / MacOS X 平台上的一个基于注释的依赖注入库,能够支持类方式,协议方式,实例对象方式,名称方式绑定,如果接触过Android开发大家可能会比较熟悉Butterknife,两者的功能是一致的,确切地说两者在很多特性上也都存在很多共同点:
我们来看下objection有哪些特性:
* "Annotation" Based Dependency Injection * Seamless support for integrating custom and external dependencies Custom Object Providers Meta Class Bindings Protocol Bindings Instance Bindings Named Bindings * Lazily instantiates dependencies* Eager Singletons * Initializer Support Default and custom arguments
objection用法 要熟悉objection的用法可以从objection的测试用例中进行了解,我们在进行源码分析的时候先从这些测试用例入手了解下objection的各个用法:
1.注入器的创建
当我们需要一个对象的时候我们都是通过JSObjectionInjector来获取,我们可以创建多个JSObjectionInjector:
JSObjectionInjector *injector = [JSObjection createInjector]
也可以创建一个JSObjectionInjector 并通过setDefaultInjector来将它作为默认的注入器:
JSObjectionInjector *injector = [JSObjection createInjector];[JSObjection setDefaultInjector:injector];
这种情况下任何地方就可以通过****[JSObjection defaultInjector]****来获取这个默认的注入器了。
它支持getObject以及下标获取两类方式:
[injector getObject:AObject.class ]; injector[AObject.class ]; [injector objectForKeyedSubscript:AObject.class ];
2.注册一个对象
objection_register (XXXX) objection_register_singleton (XXXX)
objection 可以自动完成对象的注册,但是我们也可以显式地对某个对象进行注册,objection中对象的注入有上面两种方式,最后一个是注册一个单例对象使用到的。个人建议如果不是注册单例就不用显式注册了。 objection_register和objection_register_singleton的区别是如果一旦有某个类被标记为objection_register_singleton,那么任何地方从注册器取出来的对象都是同一个。而注册成非单例的对象每次创建的时候都会返回一个新的对象。
还需要注意一点是这里的单例是针对单个注入器的,不同的注入器中返回的则是不同的对象,这点需要十分注意。
will not return the same instance per injector if object is a singleton
@interface Engine : NSObject@end @implementation Engineobjection_register(Engine) @end
@interface UnregisteredCar : NSObject@property (nonatomic, strong) Engine *engine;@end @implementation UnregisteredCarobjection_requires(@"engine" ) @synthesize engine;@end
it(@"correctly builds a registered object" , ^{ id engine = [[JSObjection defaultInjector ] getObject:[Engine class ] ]; assert That(engine , isNot (nilValue () )); }); it(@"will auto register a class if it is not explicitly registered" , ^{ UnregisteredCar *unregisteredCar = [[JSObjection defaultInjector ] getObject:[UnregisteredCar class ] ]; assert That(unregisteredCar , is (notNilValue () )); assert That(unregisteredCar .engine , is (notNilValue () )); });
每次注入的时候都会执行awakeFromObjection
@implementation Car objection_register(Car) @synthesize engine, brakes, awake;- (void )awakeFromObjection { awake = YES ; }
it(@"calls awakeFromObjection when injecting dependencies into properties of an existing instance" , ^{ Car *car = [[Car alloc ] init]; [[JSObjection defaultInjector ] injectDependencies:car]; assert ThatBool([car awake ], isTrue () ); assert ThatBool([car .engine awake ], isTrue () ); });
3. 属性注入
如果我们需要的对象里面有其他的对象那么就需要objection_requires和objection_requires_sel指定当前类中哪些子属性需要注入了,直接看例子
@interface Car : NSObject { Engine *engine; Brakes *brakes; } @property (nonatomic , strong ) Engine *engine;@property (nonatomic , strong ) Brakes *brakes;@end @implementation Car objection_register(Car) @synthesize engine, brakes, awake;objection_requires(@"engine" , @"brakes" ) @end
it(@"correctly builds and object with dependencies" , ^{ Car *car = [[JSObjection defaultInjector ] getObject:[Car class ] ]; assert That(car , isNot (nilValue () )); assert That(car .engine , isNot (nilValue () )); assert That(car .engine , is (instanceOf ([Engine class ]) )); assert That(car .brakes , isNot (nilValue () )); assert That(car .brakes , is (instanceOf ([Brakes class ]) )); });
@interface UnstoppableCar : NSObject@property (nonatomic, strong) Engine *engine;@end @implementation UnstoppableCarobjection_requires_sel(@selector (engine)) @end
it(@"correctly builds objects with selector dependencies" , ^{ UnstoppableCar *car = [[JSObjection defaultInjector ] getObject:[UnstoppableCar class ] ]; assert That(car .engine , is (instanceOf ([Engine class ]) )); });
这里个人建议使用objection_requires_sel,避免使用objection_requires这种以字符串作为参数的方式。
4. 指定构造方法获取对象
默认情况下getObject使用的是对象默认的构造方法来创建出注入的对象,但是一般情况下我们会使用不同的构造方法来创建对象,这种情况下我们要怎么处理呢?方法有两种:
方式一: 通过objection_initializer_sel
objection_initializer_sel (@selector (initWithfirstName :secondName :))
然后代码中可以通过如下几种方式的任意一种给构造方法指定参数:
JSObjectionInjector *injector = [JSObjection defaultInjector];JSObjectFactory *factory = [injector getObject:[JSObjectFactory class]];//self.dog = [factory getObjectWithArgs:[IDLDog class], @"Piter" , @"Pop" ,nil]; //self.dog = [injector getObjectWithArgs:[IDLDog class], @"Piter" , @"Pop" , nil]; //self.dog = [injector getObject:[IDLDog class] argumentList:@[@"Piter" , @"Pop" ]];
方法二是通过在getObject的时候指定初始化方法:
IDLCar *car = [[IDLCar alloc] initWithName:@"GTM" ];self.dog = [injector getObject:[IDLDog class] initializer:@selector(initWithCar:) argumentList:@[car]];
这种方式就不用objection_initializer_sel 来注解了。
个人比较偏向使用第二种方式,同时注意这里的参数类型可以是自定义的类型。
5. JSObjectFactory
JSObjectFactory和JSObjectionInjector处理的事情是一样的,实际上它内部持有JSObjectionInjector。所以这里在这块不做过多介绍。
6. JSObjectionModule && JSObjectionProvider
JSObjectionModule 和 JSObjectionProvider 为我们提供了一个数据集中绑定的场所。
我们先来看下JSObjectionModule的用法:
@interface MyModule : JSObjectionModule { BOOL _instrumentInvalidEagerSingleton; BOOL _instrumentInvalidMetaClass; } @property (weak , nonatomic , readonly ) Engine *engine;@property (weak , nonatomic , readonly ) id <GearBox> gearBox;@property (nonatomic , assign ) BOOL instrumentInvalidEagerSingleton;@property (nonatomic , assign ) BOOL instrumentInvalidMetaClass;- (id )initWithEngine:(Engine *)engine andGearBox:(id <GearBox>)gearBox; @end
重写configure方法:
@implementation MyModule @synthesize engine = _engine;@synthesize gearBox = _gearBox;@synthesize instrumentInvalidEagerSingleton=_instrumentInvalidEagerSingleton;@synthesize instrumentInvalidMetaClass = _instrumentInvalidMetaClass;- (id )initWithEngine:(Engine *)engine andGearBox:(id <GearBox>)gearBox { if ((self = [super init])) { _engine = engine; _gearBox = gearBox; } return self ; } - (void )configure { [self bind:_engine toClass:[Engine class ]]; [self bind:_gearBox toProtocol:@protocol (GearBox )]; [self bindClass:[VisaCCProcessor class ] toProtocol:@protocol (CreditCardProcessor )]; [self bindClass:[VisaCCProcessor class ] toClass:[BaseCreditCardProcessor class ]]; if (self .instrumentInvalidMetaClass) { [self bindMetaClass:(id )@"sneaky" toProtocol:@protocol (MetaCar )]; } else { [self bindMetaClass:[Car class ] toProtocol:@protocol (MetaCar )]; } if (self .instrumentInvalidEagerSingleton) { [self registerEagerSingleton:[Car class ]]; } else { [self registerEagerSingleton:[EagerSingleton class ]]; } }
首先自定义的Module需要继承JSObjectionModule,并且重写configure方法,在configure方法中可以通过bind bindClass绑定,一个对象或者类可以绑定到Class和Protocol。
-bind: 和 -bindClass: 的区别是:前者通过注入器会获取同一个实例,就是你所绑定的实例,后者每次通过注入器获取的对象都是不同的,因为绑定的是类。
通过Model方式和不通过Model方式的区别是在创建Injector的时候,使用model的时候需要在创建Injector的时候指定需要的Model:
module = ; JSObjectionInjector *injector = ; ;
但是一般建议放在load方法中
+(void)load{ JSObjectionInjector* injector = ; injector = injector ? : ; injector = ; ; }
6.0 Eager Singleton
一般Singleton会在使用的时候进行注入,但是被注册为Eager Singleton会在一开始的时候就进行注入:
@interface EagerSingleton : NSObject @end @implementation EagerSingleton objection_register_singleton(EagerSingleton) - (void )awakeFromObjection { gEagerSingletonHook = YES ; } @end
[self registerEagerSingleton: [EagerSingleton class ]];
这里需要注意的是必须是使用objection_register_singleton注册为单例的类才能注册为Eager Singleton
6.1 使用name区分同一类型的绑定
当你绑定相同的 class/protocol 时,之前的绑定会被覆盖,如果不希望被覆盖,可以使用 name 进行区分:
[self bind: _rightHeadlight toClass: [Headlight class ] named: @"RightHeadlight" ]; [self bindClass: [HIDHeadlight class ] toClass: [Headlight class ] named: @"LeftHeadlight" ];
接下来就可以在注解绑定中这样用:
@implementation ShinyCarobjection_register(ShinyCar) objection_requires_names((@{@"LeftHeadlight" :@"leftHeadlight" , @"RightHeadlight" :@"rightHeadlight" })) objection_requires(@"foglight" ) @synthesize leftHeadlight, rightHeadlight, foglight;@end
还可以在手动获取的时候这样用
- (id) getObject:(id) classOrProtocol named:(NSString*) name;
6.2 需要介入实例创建过程的情景
如果需要介入实例的构建过程,可以使用 block 回调来配置你的实例对象:
- (void )configure { [self bindBlock: ^ (JSObjectionInjector * context) { Car * car = nil; if (_instrumentNilBlock) { car = [context getObject: [SixSpeedCar class ]]; } else { car = [context getObject: [FiveSpeedCar class ]]; car.engine = (id )myEngine; } return (id )car; } toClass: [Car class ]]; }
6.3 需要外部参数来创建实例的情景
如果需要外部的一些参数来创建该实例的时候可以使用Provider:
定义一个Provider遵循JSObjectionProvider协议,重写对应的方法:
@interface DemoObjectProvider : NSObject <JSObjectionProvider >@end @implementation DemoObjectProvider -(id )provide:(JSObjectionInjector *)context arguments:(NSArray *)arguments{ AObject* ob = AObject.new; NSString * name = arguments.firstObject; ob.bj = [[BObject alloc] initWithName:name]; return ob; } @end
在Model中绑定Provider
-(void )configure { [self bindProvider:[[DemoObjectProvider alloc ] init ] toClass:AObject.class ]; }
那么参数怎么传进来呢:
JSObjectionInjector * injector = [JSObjection defaultInjector];AObject * ob1 = [injector getObject:AObject .class argumentList:@[@"linxiaohai" ]];
6.4 指定绑定的作用域
这个一般用在给单例绑定降级的时候使用
@implementation Car objection_register(Car) @synthesize engine, brakes, awake;- (void )awakeFromObjection { awake = YES ; } objection_requires(@"engine" , @"brakes" ) @end
@implementation VisaCCProcessor objection_register_singleton(VisaCCProcessor) objection_initializer(initWithCreditCardNumber:, @"Default" ) objection_requires(@"validator" ) @synthesize validator = _validator;@synthesize CCNumber;- (id )initWithCreditCardNumber:(NSString *)aCCNumber { if ((self = [super init])) { self .CCNumber = aCCNumber; } return self ; } - (void )processNumber:(NSString *)number { [super processNumber:number]; } @end
@implementation ScopeModule- (void)configure { [self bindClass:[VisaCCProcessor class] inScope :JSObjectionScopeNormal ]; [self bindClass:[Car class] inScope :JSObjectionScopeSingleton ]; [self registerEagerSingleton:[Car class] ]; } @end
测试用例:
it(@"can bind a class in singleton scope" , ^{ assertThat(injector[[Car class]] , is(sameInstance(injector[[Car class]] ))); }); it(@"can bind a class in a normal scope" , ^{ assertThat(injector[[VisaCCProcessor class]] , isNot(sameInstance(injector[[VisaCCProcessor class]] ))); });
还可以在绑定Provider/Block的时候指定作用域:
@implementation ProviderScopeModule- (void)configure { [self bindProvider:[[CarProvider alloc] init ] toClass :[Car class] inScope :JSObjectionScopeSingleton ]; [self bindProvider:[[GearBoxProvider alloc] init ] toProtocol :@protocol (GearBox) inScope :JSObjectionScopeNormal ]; } @end it(@"can bind a provider in singleton scope" , ^{ assertThat(injector[[Car class]], is(sameInstance(injector[[Car class]]))); }); it (@"can bind a provider in a normal scope" , ^{ assertThat (injector[@protocol (GearBox)], isNot(sameInstance(injector[@protocol (GearBox)]))); });
@implementation BlockScopeModule- (void)configure { [self bindBlock:^(JSObjectionInjector *context) { Car *car = [[Car alloc] init ]; return (id)car; } toClass:[Car class ] inScope :JSObjectionScopeSingleton]; [self bindBlock:^(JSObjectionInjector *context) { return [[AfterMarketGearBox alloc] init ]; } toProtocol:@protocol(GearBox) inScope:JSObjectionScopeNormal]; } @end
6.4 指定多个模块
JSObjectionInjector* injector = [JSObjection defaultInjector]; injector = injector ? : [JSObjection createInjector]; injector = [injector withModule:MyModule .new ]; injector = [injector withModules:MyModule1 .new , MyModule2.new , nil]; [JSObjection setDefaultInjector:injector ];
-withModule:方法加入的模块,会将之前相同的绑定覆盖,如果你想更换甭定、作用域等等,就可以使用该方法加入新的绑定。 既然能够加入,那么对应的,withoutModuleOfType: 将会移除之前的模块。
源码解析 objection 源码不算复杂,整个思路还是蛮清晰的,下面是我重新对它目录结构进行组织后的结果:
Entry/ JSObjectionEntry . h JSObjectionProviderEntry . h JSObjectionInjectorEntry . h JSObjectionBindingEntry . h Module/ JSObjectionModule . h Factory/ JSObjectFactory . h Injector/ JSObjectionInjector . h Utils/ JSObjectionUtils . h
上面是几个重要的模块:
Entry是一系列用于创建对象的入口
Module用于绑定数据的地方,为Injector提供对象数据源
Injector用于从对象源中取出依赖对象
Factory 其实是对Injector的一个封装
Utils 是整个代码底层的一个支持
1. Injector的创建
在使用Objection进行注入的时候需要先调用:
+ (JSObjectionInjector *)createInjector:(JSObjectionModule * )module;
来创建Injector
+ (JSObjectionInjector *)createInjector:(JSObjectionModule *)module { pthread_mutex_lock (&gObjectionMutex); @try { return [[JSObjectionInjector alloc] initWithContext:gObjectionContext andModule:module]; } @finally { pthread_mutex_unlock (&gObjectionMutex); } return nil; }
createInjector方法中会通过JSObjectionInjector 的 initWithContext:andModule:module来创建一个JSObjectionInjector返回,这里的Context在JSObjection的initialize方法中就完成了初始化。 一般我们会将createInjector创建的JSObjectionInjector 设置为默认的gGlobalInjector 这样就可以在任何地方使用了。 那么传入的Context用来做什么用的?我们继续往下看:
- (instancetype)initWithContext:(NSDictionary * )theGlobalContext andModule:(JSObjectionModule * )theModule { if ((self = [self initWithContext:theGlobalContext])) { [self configureModule:theModule]; [self initializeEagerSingletons]; } return self ; } - (instancetype)initWithContext:(NSDictionary * )theGlobalContext { if ((self = [super init ])) { _globalContext = theGlobalContext; _context = [[NSMutableDictionary alloc] init ]; _modules = [[NSMutableArray alloc] init ]; [self configureDefaultModule]; [self initializeEagerSingletons]; } return self ; } - (void)configureDefaultModule { __JSObjectionInjectorDefaultModule * module = [[__JSObjectionInjectorDefaultModule alloc] initWithInjector:self ]; [self configureModule:module]; } - (void)initializeEagerSingletons { for (NSString * eagerSingletonKey in _eagerSingletons) { id entry = [_context objectForKey:eagerSingletonKey] ? : [_globalContext objectForKey:eagerSingletonKey]; if ([entry lifeCycle] == JSObjectionScopeSingleton ) { [self getObject:NSClassFromString (eagerSingletonKey)]; } else { @throw [NSException exceptionWithName:@"JSObjectionException" reason:[NSString stringWithFormat:@"Unable to initialize eager singleton for the class '%@' because it was never registered as a singleton" , eagerSingletonKey] userInfo:nil ]; } } } - (void)configureModule:(JSObjectionModule * )module { [_modules addObject:module]; [module configure]; NSSet * mergedSet = [module.eagerSingletons setByAddingObjectsFromSet:_eagerSingletons]; _eagerSingletons = mergedSet; [_context addEntriesFromDictionary:module.bindings]; }
JSObjectionInjector初始化方法中有几个比较重要的对象_globalContext,_context,_modules _globalContext用于存储全局的绑定信息,_context存储的是当前injector的绑定信息,_modules 存储的是该injector所包含的JSObjectionModule。
在初始化JSObjectionInjector的阶段,主要完成如下工作:
1 . 将_JSObjectionInjectorDefaultModule 以及外部注入的module添加到_modules ,并调用configure 执行绑定2 . 将_JSObjectionInjectorDefaultModule 以及外部注入的module中的eagerSingletons 添加到_eagerSingletons 3 . 将module中的绑定信息添加到_context 4 . 完成EagerSingletons的初始化
2. JSObjectionModule
JSObjectionModule中有两个对象_bindings,_eagerSingletons 一个用于存放所有的绑定信息,后面用于存放Eager Singletons的。
JSObjectionModule中的所有注册绑定信息都存放在_bindings,大致归纳了下无非就只有如下几种:
- (void) bindMetaClass:(Class) metaClass toProtocol:(Protocol *) aProtocol - (void) bind:(id) instance toProtocol:(Protocol *) aProtocol named:(NSString *) name - (void) bind:(id) instance toClass:(Class) aClass named:(NSString *) name - (void) bindClass:(Class) aClass toClass:(Class) toClass inScope:(JSObjectionScope) scope named:(NSString*) name - (void) bindClass:(Class) aClass toProtocol:(Protocol *) aProtocol inScope:(JSObjectionScope) scope named:(NSString*) name - (void) bindProvider:(id <JSObjectionProvider>) provider toClass:(Class) aClass inScope:(JSObjectionScope) scope named:(NSString *) name - (void) bindProvider:(id <JSObjectionProvider>) provider toProtocol:(Protocol *) aProtocol inScope:(JSObjectionScope) scope named:(NSString *) name - (void) bindBlock:(id (^) (JSObjectionInjector *context) )block toClass:(Class) aClass inScope:(JSObjectionScope) scope named:(NSString *) name - (void) bindBlock:(id (^) (JSObjectionInjector *context) )block toProtocol:(Protocol *) aProtocol inScope:(JSObjectionScope) scope named:(NSString *) name
绑定实例对象,绑定类,绑定Provider,绑定block,绑定MetaClass。 绑定的目标对象主要有两类一个是Class 一个是Protocol。
_bindings的key有两类如果目标对象是Class则key通过方法:
- (NSString *)classKey:(Class )aClass withName:(NSString*)name { return [NSString stringWithFormat:@"%@%@%@", NSStringFromClass(aClass), name ? @":" : @"", name ? name : @""]; }
比如class名为IDLDog name为animalDomain,那么key就是@”IDLDog:animalDomain”
如果目标对象是Protocol则key通过方法:
- (NSString *)protocolKey:(Protocol *)aProtocol withName:(NSString *)name{ return [NSString stringWithFormat:@"<%@>%@%@" , NSStringFromProtocol (aProtocol), name ? @":" : @"" , name ? name : @"" ]; }
比如Protocol名为IDLDogProtocol name为animalDomain,那么key就是@”:animalDomain”
而value值是用于生成对象的Entry
bindMetaClass ---> JSObjectionBindingEntrybind ---> JSObjectionBindingEntrybindClass ---> __JSClassProvider ---> JSObjectionProviderEntrybindProvider ---> JSObjectionProviderEntrybindBlock ---> JSObjectionProviderEntry
3. getObject 向依赖注入器中获取对象
- (id )getObject:(id )classOrProtocol named:(NSString *)name initializer:(SEL)selector argumentList:(NSArray *)argumentList { @synchronized (self ) { if (!classOrProtocol) { return nil ; } NSString *key = nil ; BOOL isClass = class_isMetaClass(object_getClass(classOrProtocol)); if (isClass) { key = NSStringFromClass (classOrProtocol); } else { key = [NSString stringWithFormat:@"<%@>" , NSStringFromProtocol (classOrProtocol)]; } if (name) { key = [NSString stringWithFormat:@"%@:%@" ,key,name]; } id <JSObjectionEntry> injectorEntry = [_context objectForKey:key]; injectorEntry.injector = self ; if (!injectorEntry) { id <JSObjectionEntry> entry = [_globalContext objectForKey:key]; if (entry) { injectorEntry = [[entry class ] entryWithEntry:entry]; injectorEntry.injector = self ; [_context setObject:injectorEntry forKey:key]; } else if (isClass) { injectorEntry = [JSObjectionInjectorEntry entryWithClass:classOrProtocol scope:JSObjectionScopeNormal]; injectorEntry.injector = self ; [_context setObject:injectorEntry forKey:key]; } } if (classOrProtocol && injectorEntry) { if ([injectorEntry respondsToSelector:@selector (extractObject:initializer:)]) { return [injectorEntry extractObject:argumentList initializer:selector]; } return [injectorEntry extractObject:argumentList]; } return nil ; } return nil ; }
getObject方法比较简单,就是通过classOrProtocol,name 来构建出key。先从_context中获取如果没有则从_globalContext中获取对应的JSObjectionEntry。然后通过extractObject:initializer:或者extractObject:来获取。
4. Entrys 构建对象
我们上面归纳了bind方法与JSObjectionEntry之间的对应关系:
bindMetaClass ---> JSObjectionBindingEntrybind ---> JSObjectionBindingEntrybindClass ---> __JSClassProvider ---> JSObjectionProviderEntrybindProvider ---> JSObjectionProviderEntrybindBlock ---> JSObjectionProviderEntry
JSObjectionBindingEntry
@implementation JSObjectionBindingEntry - (instancetype )initWithObject:(id )theObject { if ((self = [super init])) { _instance = theObject; } return self ; } - (id )extractObject:(NSArray *)arguments { return _instance; } - (JSObjectionScope)lifeCycle { return JSObjectionScopeSingleton; } - (void )dealloc { _instance = nil ; } @end
JSObjectionBindingEntry 中用于绑定已经创建好的对象,extractObject方法实际上返回的就是最初传入的_instance
JSObjectionProviderEntry
@implementation JSObjectionProviderEntry @synthesize lifeCycle = _lifeCycle;- (id)initWithProvider:(id< JSObjectionProvider > )theProvider lifeCycle:(JSObjectionScope )theLifeCycle { if ((self = [super init ])) { _provider = theProvider; _lifeCycle = theLifeCycle; _storageCache = nil ; } return self ; } - (id)initWithBlock:(id(^ )(JSObjectionInjector * context))theBlock lifeCycle:(JSObjectionScope )theLifeCycle { if ((self = [super init ])) { _block = [theBlock copy]; _lifeCycle = theLifeCycle; _storageCache = nil ; } return self ; } - (id)extractObject:(NSArray * )arguments { if (self .lifeCycle == JSObjectionScopeNormal || ! _storageCache) { return [self buildObject:arguments]; } return _storageCache; } - (void)dealloc { _storageCache = nil ; } - (id)buildObject:(NSArray * )arguments { id objectUnderConstruction = nil ; if (_block) { objectUnderConstruction = _block(self .injector); } else { objectUnderConstruction = [_provider provide:self .injector arguments:arguments]; } if (self .lifeCycle == JSObjectionScopeSingleton ) { _storageCache = objectUnderConstruction; } return objectUnderConstruction; } @end
JSObjectionProviderEntry 有两种构造方法,一种是通过Provider,一种是通过Block,JSObjectionProviderEntry 内部会缓存一个_storageCache,在生命周期为JSObjectionScopeSingleton 的情况下extractObject会使用_storageCache 否则会在buildObject中通过_block或者_provider创建创建对象返回。
5.宏定义
5.1 注册依赖
+ (void )initialize { \ if (self == [value class ]) { \ [JSObjection registerClass:[value class ] scope: JSObjectionScopeNormal]; \ } \ } + (void )initialize { \ if (self == [value class ]) { \ [JSObjection registerClass:[value class ] scope: JSObjectionScopeSingleton]; \ } \ }
我们上面提到了注册依赖可以通过objection_register以及objection_register_singleton前者用于注册正常的类,后者用于注册单例,但是底层都是通过:
JSObjection registerClass:scope :
来注册的只不过作用域参数不一样罢了。
+ (void)registerClass:(Class)theClass scope:(JSObjectionScope)scope { pthread_mutex_lock(&gObjectionMutex ) ; if (theClass && [gObjectionContext objectForKey :NSStringFromClass (theClass )] == nil) { [gObjectionContext setObject :[JSObjectionInjectorEntry entryWithClass :theClass scope :scope ] forKey:NSStringFromClass(theClass ) ]; } pthread_mutex_unlock(&gObjectionMutex ) ; }
这种情况下的Entry和上面的就不大一样了它是JSObjectionInjectorEntry 。被添加到gObjectionContext。
@implementation JSObjectionInjectorEntry #pragma mark - Instance Methods - (instancetype) extractObject:(NSArray *)arguments initializer :(SEL)initializer { if (self.lifeCycle == JSObjectionScopeNormal || !_storageCache) { return [self buildObject :arguments initializer : initializer ] ; } return _storageCache; } #pragma mark - Private Methods - (id)buildObject:(NSArray *)arguments initializer : (SEL) initializer { id objectUnderConstruction = nil; if (initializer != nil) { objectUnderConstruction = JSObjectionUtils . buildObjectWithInitializer(self .classEntry , initializer , arguments ) ; } else if ([self .classEntry respondsToSelector :@selector (objectionInitializer )] ) { objectUnderConstruction = JSObjectionUtils . buildObjectWithInitializer(self .classEntry , [self initializerForObject ], [self argumentsForObject :arguments ]) ; } else { objectUnderConstruction = [[self .classEntry alloc ] init]; } if (self.lifeCycle == JSObjectionScopeSingleton) { _storageCache = objectUnderConstruction; } JSObjectionUtils . injectDependenciesIntoProperties(self .injector , self .classEntry , objectUnderConstruction ) ; return objectUnderConstruction; } - (SEL)initializerForObject { return NSSelectorFromString([[self .classEntry objectionInitializer ] objectForKey :JSObjectionInitializerKey]) ; } - (NSArray *)argumentsForObject:(NSArray *)givenArguments { return givenArguments.count > 0 ? givenArguments : [[self .classEntry objectionInitializer ] objectForKey:JSObjectionDefaultArgumentsKey]; } #pragma mark - Class Methods + (id)entryWithClass:(Class)theClass scope:(JSObjectionScope)theLifeCycle { return [[JSObjectionInjectorEntry alloc ] initWithClass:theClass lifeCycle:theLifeCycle]; } + (id)entryWithEntry:(JSObjectionInjectorEntry *)entry { return [[JSObjectionInjectorEntry alloc ] initWithClass:entry.classEntry lifeCycle:entry.lifeCycle]; } @end
上面最关键的代码在buildObject中如果没有指定参数则调用:
objectUnderConstruction = [[self.classEntry alloc] init]
也就是走默认构造方法。
如果有指定构造方法则走下面方法:
JSObjectionUtils . buildObjectWithInitializer(self .classEntry , initializer , arguments ) ;
当然还可以实现objectionInitializer方法来指定对应的构造方法。
JSObjectionUtils.buildObjectWithInitializer 方法很简单就是通过消息分发机制调用构造方法:
static id BuildObjectWithInitializer(Class klass, SEL initializer, NSArray *arguments) { NSMethodSignature *signature = [klass methodSignatureForSelector:initializer]; __autoreleasing id instance = nil; BOOL isClassMethod = signature != nil && initializer != @selector(init); if (!isClassMethod) { instance = [klass alloc]; signature = [klass instanceMethodSignatureForSelector:initializer]; } if (signature) { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget:isClassMethod ? klass : instance]; [invocation setSelector:initializer]; for (int i = 0; i < arguments.count; i++) { __unsafe_unretained id argument = [arguments objectAtIndex:i]; [invocation setArgument:&argument atIndex:i + 2]; } [invocation invoke]; [invocation getReturnValue:&instance]; return instance; } else { @throw [NSException exceptionWithName:JSObjectionException reason:[NSString stringWithFormat:@"Could not find initializer '%@' on %@" , NSStringFromSelector(initializer), NSStringFromClass(klass)] userInfo:nil]; } return nil; }
构造完需要的对象后还需要通过JSObjectionUtils.injectDependenciesIntoProperties 来使用依赖对属性进行注入:
static void InjectDependenciesIntoProperties(JSObjectionInjector * injector , Class klass , id object ) { if ([klass respondsToSelector :@selector (objectionRequires )] ) { NSSet *properties = [klass objectionRequires ] ; NSMutableDictionary *propertiesDictionary = [NSMutableDictionary dictionaryWithCapacity :properties .count ] ; for (NSString *propertyName in properties) { JSObjectionPropertyInfo propertyInfo; id desiredClassOrProtocol; _getPropertyInfo(klass , propertyName , &propertyInfo , &desiredClassOrProtocol ) ; id theObject = [injector getObject :desiredClassOrProtocol ] ; _validateObjectReturnedFromInjector(&theObject , propertyInfo , desiredClassOrProtocol , propertyName ) ; [propertiesDictionary setObject :theObject forKey :propertyName ] ; } [object setValuesForKeysWithDictionary :propertiesDictionary ] ; } if ([klass respondsToSelector :@selector (objectionRequiresNames )] ) { NSDictionary *namedProperties = [klass objectionRequiresNames ] ; NSMutableDictionary *propertiesDictionary = [NSMutableDictionary dictionaryWithCapacity :namedProperties .count ] ; for (NSString *namedPropertyKey in [namedProperties allKeys ] ) { NSString* propertyName = [namedProperties valueForKey :namedPropertyKey ] ; JSObjectionPropertyInfo propertyInfo; id desiredClassOrProtocol; _getPropertyInfo(klass , propertyName , &propertyInfo , &desiredClassOrProtocol ) ; id theObject = [injector getObject :desiredClassOrProtocol named :namedPropertyKey ] ; _validateObjectReturnedFromInjector(&theObject , propertyInfo , desiredClassOrProtocol , propertyName ) ; [propertiesDictionary setObject :theObject forKey :propertyName ] ; } [object setValuesForKeysWithDictionary :propertiesDictionary ] ; } if ([object respondsToSelector :@selector (awakeFromObjection )] ) { [object awakeFromObjection ] ; } }
5.2 属性依赖
+ (NSSet *)objectionRequires { \ NSSet *requirements = [NSSet setWithObjects: args, nil]; \ return JSObjectionUtils.buildDependenciesForClass(self, requirements); \ } + (NSSet *)objectionRequires { \ SEL selectors[] = {args}; \ NSMutableSet *requirements = [NSMutableSet set]; \ for (NSUInteger j = 0 ; j < sizeof(selectors)/ sizeof(SEL); j++) { \ SEL selector = selectors[j]; \ [requirements addObject:NSStringFromSelector(selector)]; \ } \ return JSObjectionUtils.buildDependenciesForClass(self, requirements); \ } + (NSDictionary *)objectionRequiresNames { \ return JSObjectionUtils.buildNamedDependenciesForClass(self, namedDependencies); \ }
这里就是上面提到的用于指定需要属性注入的方法,不做展开介绍了。
5.3 指定初始化方法
#define objection_initializer_sel(selectorSymbol , args ... ) \ + (NSDictionary *)objectionInitializer { \ id objs[] = {args}; \ NSArray *defaultArguments = [NSArray arrayWithObjects : objs count :sizeof (objs )/ sizeof (id )] ; \ return JSObjectionUtils . buildInitializer(selectorSymbol , defaultArguments ) ; \ }
这个也是为了buildObject提供的。用于指定构造方法,细节可以看buildObject中。