开源库信息

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 Engine
objection_register(Engine)
@end

@interface UnregisteredCar : NSObject
@property(nonatomic, strong) Engine *engine;
@end

@implementation UnregisteredCar
objection_requires(@"engine")
@synthesize engine;
@end
it(@"correctly builds a registered object", ^{
id engine = [[JSObjection defaultInjector] getObject:[Engine class]];
assertThat(engine, isNot(nilValue()));
});

it(@"will auto register a class if it is not explicitly registered", ^{
UnregisteredCar *unregisteredCar = [[JSObjection defaultInjector] getObject:[UnregisteredCar class]];
assertThat(unregisteredCar, is(notNilValue()));
assertThat(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];
assertThatBool([car awake], isTrue());
assertThatBool([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]];
assertThat(car, isNot(nilValue()));
assertThat(car.engine, isNot(nilValue()));
assertThat(car.engine, is(instanceOf([Engine class])));
assertThat(car.brakes, isNot(nilValue()));
assertThat(car.brakes, is(instanceOf([Brakes class])));
});

@interface UnstoppableCar : NSObject
@property(nonatomic, strong) Engine *engine;
@end

@implementation UnstoppableCar
objection_requires_sel(@selector(engine))
@end
it(@"correctly builds objects with selector dependencies", ^{
UnstoppableCar *car = [[JSObjection defaultInjector] getObject:[UnstoppableCar class]];
assertThat(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 = [[MyModule alloc] initWithEngine:engine andGearBox:gearBox];    
JSObjectionInjector *injector = [JSObjection createInjector:module];
[JSObjection setDefaultInjector:injector];

但是一般建议放在load方法中

+(void)load{
JSObjectionInjector* injector = [JSObjection defaultInjector];
injector = injector ? : [JSObjection createInjector];
injector = [injector withModule:[self.class new]];
[JSObjection setDefaultInjector: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 ShinyCar
objection_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 {
//这里绑定有JSObjectFactory
__JSObjectionInjectorDefaultModule *module = [[__JSObjectionInjectorDefaultModule alloc] initWithInjector:self];
//将__JSObjectionInjectorDefaultModule添加到_modules,调用__JSObjectionInjectorDefaultModule的configure方法(__JSObjectionInjectorDefaultModule没有重写configure)。
//将__JSObjectionInjectorDefaultModule中的eagerSingletons合并到_eagerSingletons
//将__JSObjectionInjectorDefaultModule中的bindings添加到_context
[self configureModule:module];
}

- (void)initializeEagerSingletons {
for (NSString *eagerSingletonKey in _eagerSingletons) {
id entry = [_context objectForKey:eagerSingletonKey] ?: [_globalContext objectForKey:eagerSingletonKey];
if ([entry lifeCycle] == JSObjectionScopeSingleton) {
//创建eagerSingletonKey对象
[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 {
//将model添加到_modules
[_modules addObject:module];
//调用module的configure执行绑定
[module configure];
//将当前的model的eagerSingletons合并到_eagerSingletons
NSSet *mergedSet = [module.eagerSingletons setByAddingObjectsFromSet:_eagerSingletons];
_eagerSingletons = mergedSet;
//将当前model的module.bindings添加到_context
[_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 ---> JSObjectionBindingEntry
bind ---> JSObjectionBindingEntry
bindClass ---> __JSClassProvider ---> JSObjectionProviderEntry
bindProvider ---> JSObjectionProviderEntry
bindBlock ---> JSObjectionProviderEntry

3. getObject 向依赖注入器中获取对象

- (id)getObject:(id)classOrProtocol named:(NSString*)name initializer:(SEL)selector argumentList:(NSArray *)argumentList {
@synchronized(self) {

//获取key
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];
}

//从_context中获取对应的Entry
id<JSObjectionEntry> injectorEntry = [_context objectForKey:key];
injectorEntry.injector = self;
if (!injectorEntry) {
//如果没有JSObjectionEntry 则 从_globalContext中获取
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:)]) {
//调用extractObject获取对象
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 ---> JSObjectionBindingEntry
bind ---> JSObjectionBindingEntry
bindClass ---> __JSClassProvider ---> JSObjectionProviderEntry
bindProvider ---> JSObjectionProviderEntry
bindBlock ---> 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 注册依赖

#define objection_register(value)			\
+ (void)initialize { \
if (self == [value class]) { \
[JSObjection registerClass:[value class] scope: JSObjectionScopeNormal]; \
} \
}

#define objection_register_singleton(value) \
+ (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) {

//如果当前有通过objectionRequires指定需要注入的属性
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];
}
//如果当前有通过objectionRequiresNames指定需要注入的属性
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];
}
//调用awakeFromObjection
if ([object respondsToSelector:@selector(awakeFromObjection)]) {
[object awakeFromObjection];
}
}

5.2 属性依赖

#define objection_requires(args...) \
+ (NSSet *)objectionRequires { \
NSSet *requirements = [NSSet setWithObjects: args, nil]; \
return JSObjectionUtils.buildDependenciesForClass(self, requirements); \
}

#define objection_requires_sel(args...) \
+ (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); \
}

#define objection_requires_names(namedDependencies) \
+ (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中。

Contents
  1. 1. 开源库信息
  2. 2. objection用法
  3. 3. 源码解析