一、简介
Objection
是一个iOS
中轻量级的对DI
及IoC
的实现,不知道DI
及IoC
的请移步至iOS组件通信方案,Objection
只有Object-C
版本,没有Swift
版本,如果你想在Swift
中使用这种容器框架,可以了解下Typhoon
,二者对于DI
的实现方式不同,原理也不同,本篇文章主要分析Objection
。
它的优点有哪些呢?
- 支持我们进行面向接口编程,有利于项目解耦(组件化)。
- 对象的销毁由系统管理,而
Objection
可以帮助我们完成对象的创建工作。 - 其他优点自行体会。
二、关键词及其实现
提前了解关键词有助于我们更好的理解该框架的底层实现,Objection
中的关键词有如下几个:
-
Annocation
(注解) KVC
runtime
1.先来说注解
Objection
中的注解是通过宏定义来实现的,所以掌握注解的使用至关重要,这些宏定义全部放在了Objection.h
文件中,包括如下几个:
(1)objection_register(value)
向容器中注册类(Class
),未来使用的时候,容器会直接拿到这个类然后进行实例化。
(2)objection_register_singleton(value)
功能同(1),不过这里是注册单例。
(3)objection_requires(args...)
基于属性的注入方式依赖此宏,这个宏用于定义某个类进行实例化时所依赖的属性,如果这个属性所属的类是我们之前已经注入到容器中的,那么容器就会取出这个类的实例对象,并将实例对象赋值给这个属性。
(4)objection_requires_sel(args...)
这个也是基于属性的注入方式,和(3)是一样的,只不过在你获取或注入某个属性值的时候,会走getter
方法。
(5)objection_requires_names(namedDependencies)
这个是对(3)的功能补充,用于给注入的属性起个别名。
(6)objection_initializer_sel(selectorSymbol, args...)
基于构造器的注入方式依赖此宏,这个宏用于定义某个类的初始化函数,可以看到展开后有两个参数,第一个参数是你自定义的初始化函数,第二个参数是个可变类型的参数,也就是你初始化函数所依赖的参数需要在这里指明,举个例子:
Person.m
- (instance)initWithName:(NSString *)name age:(NSString *)age {
if(self = [super init]){
self.name = name;
self.age = age;
}
return self;
}
objection_initializer_sel(initWithName:age:)
(7)objection_initializer(selectorSymbol, args...)
这个宏内部会调用(6)
2.KVC
Objection
中对于注入功能的实现是通过KVC
来完成的,这也是容器完成创建实例对象的最后一步,如下所示。
- (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;
}
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];
}
//使用KVC注入属性值
[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];
}
//使用KVC注入属性值
[object setValuesForKeysWithDictionary:propertiesDictionary];
}
if ([object respondsToSelector:@selector(awakeFromObjection)]) {
//如果对象重写了awakeFromObjection方法,这里还会调用awakeFromObjection方法,方便你做一些其他的操作
[object awakeFromObjection];
}
}
(3)runtime
方法的调用过程中使用了大量runtime
技术,比如:
- 遍历类的属性
- 反射
- 消息传递
三、整体架构
了解其架构设计可以更好的帮助我们理解其实现原理。
我画了一个草图用来表示Objection
主要的工作流程,如下:
1.总体流程
入口类是JSObjection
,有几个常用的方法是:
+ (JSObjectionInjector *)createInjectorWithModules:(JSObjectionModule *)first, ... NS_REQUIRES_NIL_TERMINATION;
+ (JSObjectionInjector *)createInjectorWithModulesArray:(NSArray *)modules;
+ (JSObjectionInjector *)createInjector:(JSObjectionModule *)module;
+ (JSObjectionInjector *)createInjector;
通过这几个方法可以看出,JSObjection
会根据若干个Module
实例化出Injector
对象,对应最左侧那一条流程,也就是JSObjection
会收集所有Module
对象,然后根据这些Module
对象实例化出Injector
对象。
对于开发者来说,操作JSObjectionModule
对象可能是最常见的操作之一,顾名思义,JSObjectionModule
代表一个模块,怎么划分模块任你设计,你可以将不同的页面划分成不同的模块,比如一个商品详情页面可以对应一个Module
,一个会话详情页面可以对应一个Module
;也可以将一条业务线划分成一个Module
,比如商品Module
可以包括列表、详情、订单等页面,一个即时通讯Module
可以包括会话列表、会话详情、个人详情等页面。
JSObjectionModule
包含哪些操作呢?很多很多,多到你用不过来。
- (void)bind:(id)instance toClass:(Class)aClass;
- (void)bind:(id)instance toClass:(Class)aClass named:(NSString *)name;
- (void)bind:(id)instance toProtocol:(Protocol *)aProtocol;
- (void)bind:(id)instance toProtocol:(Protocol *)aProtocol named:(NSString *)name;
- (void)bindMetaClass:(Class)metaClass toProtocol:(Protocol *)aProtocol;
- (void)bindProvider:(id)provider toClass:(Class)aClass;
- (void)bindProvider:(id)provider toClass:(Class)aClass named:(NSString *)name;
- (void)bindProvider:(id)provider toProtocol:(Protocol *)aProtocol;
- (void)bindProvider:(id)provider toProtocol:(Protocol *)aProtocol named:(NSString *)name;
- (void)bindProvider:(id)provider toClass:(Class)aClass inScope:(JSObjectionScope)scope;
- (void)bindProvider:(id)provider toClass:(Class)aClass inScope:(JSObjectionScope)scope named:(NSString *)name;
- (void)bindProvider:(id)provider toProtocol:(Protocol *)aProtocol inScope:(JSObjectionScope)scope;
- (void)bindProvider:(id)provider toProtocol:(Protocol *)aProtocol inScope:(JSObjectionScope)scope named:(NSString *)name;
- (void)bindClass:(Class)aClass toProtocol:(Protocol *)aProtocol;
- (void)bindClass:(Class)aClass toProtocol:(Protocol *)aProtocol named:(NSString*)name;
- (void)bindClass:(Class)aClass toProtocol:(Protocol *)aProtocol inScope:(JSObjectionScope)scope named:(NSString*)name;
- (void)bindClass:(Class)aClass toClass:(Class)toClass;
- (void)bindClass:(Class)aClass toClass:(Class)toClass named:(NSString*)name;
- (void)bindClass:(Class)aClass toClass:(Class)toClass inScope:(JSObjectionScope)scope named:(NSString*)name;
- (void)bindBlock:(id (^)(JSObjectionInjector *context))block toClass:(Class)aClass;
- (void)bindBlock:(id (^)(JSObjectionInjector *context))block toClass:(Class)aClass named:(NSString *)name;
- (void)bindBlock:(id (^)(JSObjectionInjector *context))block toProtocol:(Protocol *)aProtocol;
- (void)bindBlock:(id (^)(JSObjectionInjector *context))block toProtocol:(Protocol *)aProtocol named:(NSString *)name;
- (void)bindBlock:(id (^)(JSObjectionInjector *context))block toClass:(Class)aClass inScope:(JSObjectionScope)scope;
- (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;
- (void)bindBlock:(id (^)(JSObjectionInjector *context))block toProtocol:(Protocol *)aProtocol inScope:(JSObjectionScope)scope named:(NSString *)name;
- (void)bindClass:(Class)aClass inScope:(JSObjectionScope)scope;
- (void)registerEagerSingleton:(Class)aClass;
- (BOOL)hasBindingForClass:(Class)aClass;
- (BOOL)hasBindingForClass:(Class)aClass withName:(NSString*)name;
- (BOOL)hasBindingForProtocol:(Protocol *)protocol;
- (BOOL)hasBindingForProtocol:(Protocol *)protocol withName:(NSString*)name;
- (void)configure;
可以看出Module
支持各种绑定操作:
- 我们可以将
Class
和Protocol
绑定在一起,通过protocol
拿到Class
的实例对象,此种方式为我们进行面向接口编程提供了很好的支持。
举个例子,假设现在有个Car
类,我们使用Car
类实例化出一个对象,使用JSObjection
的代码应该是这个样子的:
//CarDao
@protocol CarDao
- (void)fire; //启动
@end
//Car
@interface Car : NSObject
@end
//绑定
JSObjectionModule *module = [[JSObjectionModule alloc] init];
[module bindClass:[Car class] toProtocol:@protocol(CarDao)];
//获取
id carDao = [[JSObjection defaultInjector] getObject:@protocol(CarDao)];
[carDao fire];
- 可以将
instance
和Class
/Protocol
绑定在一起 - 可以将实现了
JSObjectionProvider
接口的对象和Class
/Protocol
绑定在一起,在该对象中实现接口方法- (id)provide:(JSObjectionInjector *)context arguments:(NSArray *)arguments
,在这个方法中实例化你需要的对象。 - 还可以将
block
和Class
/Protocol
绑定在一起,block
的返回值就是你需要的实例化对象。 - 最后,我们也可以从
JSObjectionModule
派生出子类,重写configure
方法,在这个方法中进行绑定操作。
绑定的下一步操作做什么?
在JSObjectionModule
中会把你需要绑定的数据封装成JSObjectionEntry
对象(这个类还有3个子类,分别是JSObjectionProviderEntry
、JSObjectionInjectorEntry
、JSObjectionBindingEntry
),然后把这个对象作为value
,Class
/Protocol
作为key
存放进一个bindings
字典容器中。针对绑定的数据类型不通,封装所使用的子类类型也不同,总结就是:
(1)如果绑定的是instance
,就会使用JSObjectionBindingEntry
进行封装;
(2)如果绑定的是实现了JSObjectionProvider
接口的对象,亦或是Class
-Protocol
,都会使用JSObjectionProviderEntry
进行封装;
(3)其他都使用JSObjectionInjectorEntry
进行封装。
而JSObjectionInjector
中也有一个字典容器变量context
,JSObjectionInjector
会将Module
管理的bindings
数据添加到context
中,这样,我们后续便可以通过JSObjectionInjector
对象获取到最初通过Module
绑定的数据。
2.实例对象构建流程
说到对象的构建流程,不得不再把JSObjectionEntry
拿出来,在这个类文件中,还有一个同名的JSObjectionEntry
协议(接口),如下:
@protocol JSObjectionEntry
@property (nonatomic, readonly) JSObjectionScope lifeCycle; //声明周期
@property (nonatomic, assign) JSObjectionInjector *injector; //注入者
@required
- (id)extractObject:(NSArray *)arguments; //根据args参数实例化出对象
+ (id)entryWithEntry:(JSObjectionEntry *)entry;
@optional
-(id)extractObject:(NSArray *)arguments initializer: (SEL)initializer; //根据args参数实例化出对象
@end
知道这个协议中每个属性及方法的作用对于构建实例对象至关重要。
父类遵守了这套协议,并对这几个方法创建了默认的实现,而派生自JSObjectionEntry
的3个子类均对这套协议的方法有着不同的实现,为什么会有不同的实现?因为每个子类绑定的数据类型不同,因此数据类型不同,实例化的方式也不同。
一个一个来看。
(1)JSObjectionBindingEntry
- (id)extractObject:(NSArray *)arguments {
return _instance;
}
JSObjectionBindingEntry
对接口方法的时间很简单,直接把绑定的instance
返回就好了。
(2)JSObjectionProviderEntry
- (id)extractObject:(NSArray *)arguments {
if (self.lifeCycle == JSObjectionScopeNormal || !_storageCache) {
return [self buildObject:arguments];
}
return _storageCache;
}
- (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;
}
如果你是通过JSObjectionModule
的bindBlock
方法进行绑定操作,那么就会执行objectUnderConstruction = _block(self.injector);
,直接把block
中return
的数据返回给你。
如果你是通过JSObjectionModule
的bindProvider
方法进行绑定操作,那么就会执行objectUnderConstruction = [_provider provide:self.injector arguments:arguments];
,找到你曾经绑定过的并实现了JSObjectionProvider
接口的自定义对象,再调用privide:arguments:
方法,这个方法实现中会返回你所实例化的对象。代码示例如下:
@implementation CarProvider
- (id)provide:(JSObjectionInjector *)context arguments:(NSArray *)arguments {
Car *car = [context getObject:[Car class]];
return car;
}
@end
(3)JSObjectionInjectorEntry
- (instancetype)extractObject:(NSArray *)arguments {
return [self extractObject:arguments initializer:nil];
}
- (instancetype) extractObject:(NSArray *)arguments initializer:(SEL)initializer {
if (self.lifeCycle == JSObjectionScopeNormal || !_storageCache) {
return [self buildObject:arguments initializer: initializer];
}
return _storageCache;
}
- (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;
}
这个类实现的接口方法是最复杂的,也充分体现了DI
和IoC
的思想。
从最后一个方法调用中,我们可以看出思路大致如下:
(1)这个方法有两个入参,分别是完整实例化依赖的参数和构造方法。
(2)先判断绑定的类有没有通过注解自定义构造方法,如果有,就会执行JSObjectionUtils.buildObjectWithInitializer(self.classEntry, initializer, arguments);
,Class
有了,类的构造方法有了,构造方法依赖的参数也有了,有了这些就可以创建出一个完整的实例对象。
(3)如果没有通过注解自定义构造方法,会判断你有没有通过使用objection_initializer_sel
注解,这一步的处理逻辑和(2)是相同的。
(4)如果(2)和(3)都没有,就会使用alloc init
进行初始化操作。
(5)最后会使用JSObjectionUtils.injectDependenciesIntoProperties(self.injector, self.classEntry, objectUnderConstruction);
将对象初始化依赖的属性值注入进去。
(6)如果某个类的属性是另外一个类,就会递归执行这个流程,确保所有属性都是经过初始化的。
更多的实现细节可以查看JSObjectionUtils
这个结构体。
3.从容器获取实例
最开始我们提到,JSObjection
会收集所有的JSObjectionModule
创建出JSObjectionInjector
对象,相当于JSObjectionInjector
对象保存了我们绑定的数据(并不是所有数据,所以一定要注意使用正确的injector
对象获取绑定的数据),所以想要获取IoC
容器为什么创建的实例对象,我们也要通过JSObjectionInjector
对象来获取,确切说应该是injector
对象的这些方法:
- (id)getObject:(id)classOrProtocol;
- (id)getObject:(id)classOrProtocol named:(NSString*)name;
- (id)getObjectWithArgs:(id)classOrProtocol, ... NS_REQUIRES_NIL_TERMINATION;
- (id)getObject:(id)classOrProtocol namedWithArgs:(NSString*)name, ... NS_REQUIRES_NIL_TERMINATION;
- (id)getObject:(id)classOrProtocol arguments:(va_list)argList;
- (id)getObject:(id)classOrProtocol named:(NSString*)name arguments:(va_list)argList;
- (id)getObject:(id)classOrProtocol argumentList:(NSArray *)argumentList;
- (id)getObject:(id)classOrProtocol initializer:(SEL)selector argumentList:(NSArray *)argumentList;
- (id)getObject:(id)classOrProtocol named:(NSString*)name argumentList:(NSArray *)argumentList;
- (id)getObject:(id)classOrProtocol named:(NSString*)name initializer:(SEL)selector argumentList:(NSArray *)argumentList;
虽然方法有点多,但这些方法最后都会统一调用最后一个方法:
- (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 injectorEntry = [_context objectForKey:key];
injectorEntry.injector = self;
if (!injectorEntry) {
id 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;
}
在最后一个方法中,获取实例变量的流程是这样的:
(1)根据Class
或者Protocol
,还有别名生成key
。
(2)拿着key
先到context
中去取数据,取到的数据是JSObjectionEntry
类型的数据(因为这是一个父类),并且JSObjectionEntry
实现了JSObjectionEntry
协议,因此取到的数据会用id
这种匿名方式去接收。
(3)如果数据不存在,就到globalContext
中去取(相当于二级缓存),如果取到对象,就放入context
(一级缓存)中,如果没取到对象,就创建一个JSObjectionInjectorEntry
对象并放入context
中。
(4)如果经过(2)和(3)我们获取到了一个id
匿名对象,就会调用这个匿名对象的extractObject:initializer:
或extractObject:
方法,这两个方法的具体实现在上面我们已经讲过了,在这两个方法中会创建实例对象,最终会将这个实例对象返回,我们通过IoC
容器拿到的,就是这个实例对象。
(5)异常情况都会返回nil。
流程图中间的位置有一条蓝色虚线连接左右两部分,对应着上面所讲述的流程。
四、JSObjection
的使用
(1)构造方法注入
定义接口:
@protocol CarDao
@property(nonatomic, copy) NSString *color; //汽车的颜色
- (void)fire; //启动
@end
定义接口实现类:
@interface Car : NSObject
@end
@implementation Car
//构造方法注入
- (instancetype)initWithColor:(NSString *)color {
if (self = [super init]) {
self.color = color;
}
return self;
}
//构造方法注入
objection_initializer(initWithColor:)
@synthesize color = _color;
- (void)fire {
NSLog(@"我开着我%@的小汽车走了.", _color);
}
@end
绑定Class
-Protocol
:
JSObjectionModule *module = [[JSObjectionModule alloc] init];
[module bindClass:[Car class] toProtocol:@protocol(CarDao)];
JSObjectionInjector *injector = [JSObjection createInjector:module];
[JSObjection setDefaultInjector:injector];
获取匿名对象(也就是实现了CarDao
接口的Car
实例对象):
id carDao = [[JSObjection defaultInjector] getObjectWithArgs:@protocol(CarDao), @"黑色", nil];
[carDao fire];
(2)属性注入
CarDao
代码不变,对Car
进行一些改造:
@implementation Car
- (instancetype)init {
if (self = [super init]) {
_color = @"蓝色";
}
return self;
}
@synthesize color = _color;
- (void)fire {
NSLog(@"我开着我%@的小汽车走了.", _color);
}
@end
PersonDao
:
@protocol PersonDao
@property(nonatomic, strong) id car; //汽车
@optional
- (void)info; //个人信息
@end
Person
:
@interface Person : NSObject
@end
@implementation Person
//属性注入
objection_requires(@"car")
@synthesize car = _car;
- (void)info {
NSLog(@"-------------------------");
[_car fire];
NSLog(@"-------------------------");
}
@end
绑定及获取:
- (void)viewDidLoad {
[super viewDidLoad];
JSObjectionModule *module = [[JSObjectionModule alloc] init];
[module bindClass:[Person class] toProtocol:@protocol(PersonDao)];
[module bindClass:[Car class] toProtocol:@protocol(CarDao)];
JSObjectionInjector *injector = [JSObjection createInjector:module];
[JSObjection setDefaultInjector:injector];
//构造方法注入
id personDao = [[JSObjection defaultInjector] getObject:@protocol(PersonDao)];
[personDao info];
}
完结。
对
Typhoon
的补充
对于文章中提到的另一个IoC
容器Typhoon
,这里做一个简单的原理分析吧,Typhoon
提供的功能非常接近于Java
的Spring
框架,包括AOP & DI & IoC
。
Typhoon
在info.plist
文件中维护了一个表,表名叫TyphoonInitialAssemblies
,如下:
每个item
中的字符串数据都可以经过反射生成项目中的类。
这些类的名称是以Assembly
为结尾的,意为部件
。
Assembly
管理着众多的Definition
,Definition
意为定义
。
我们创建的类都以Assembly
为结尾,表示部件(组件),在部件类中,我们会使用TyphoonDefinition
中去关联要注入的Controller/view/other objc
,并指定这些对象初始化所需要的初始化构造方法/属性注入/方法注入等,所以我们可以知道Assembly
和Definition
是一对多关系。
在程序启动的时候首先会执行main
函数:
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([PFAppDelegate class]));
}
}
UIApplication
会设置AppDelegate
对象,设置会调用setAppDelegate:
方法,于是Typhoon
就hook
住了这个方法,在替换的方法实现中,会读取当前bundle
的info.plist
文件,获取到TyphoonInitialAssemblies
配置的内容,因为这个配置项是个数组,里面存储的是Class
对应的字符串,所以获取到这些类的字符串之后就会通过反射获取到类,再将这些类放入到数组中,如果数组count>0
就会通过这个数组实例化TyphoonComponentFactory
对象,这个工厂对象有个私有属性_registry
,里面保存的就是我们通过TyphoonDefinition
注入的模块。
获取到他们的Definition
之后,会将其添加到TyphoonComponentFactory
(组件工厂)中,等到我们取得时候,也是到这个组件工厂中去取。
所以使用Typhoon
的时候就要维护好你写的代码,因为Typhoon hook
住了你当前应用程序的入口,如果在这个过程中,因为你写的代码造成了程序异常,程序可能就会无法正常启动。