本帖最后由 Simpon 于 2016-10-19 11:44 编辑
• IoC简介 1、IoC的基本概念 IoC是Inversion of Control(控制反转)的英文缩写,它是在面向对象编程中用来降低计算机应用程序耦合度的一个重要的设计思想。 2、如何理解IoC 接下来通过一个简单的例子来理解究竟什么是IoC。 陈某希如何找女朋友? 通常情况下,我们都是根据自己的需求,确定自己在什么时候需要女朋友,以及需要什么样的女朋友。 对应到代码中,体现如下: - #import <Foundation/Foundation.h>
- #import " ZhouMouTong.h"
-
- @interface ChenMouXi: NSObject
- @property(nonatomic,strong) ZhouMouTong *gf;
- @end
复制代码
不难发现,这个时候对ChenMouXi 类和ZhouMouTong类进行了强关联,ChenMouXi对ZhouMouTong直接耦合在了一起。 但是我们知道,某希哥换女朋友的速度是无可比拟的,所以这么在代码中这么些明显不够明智,因为一旦某希哥需要换女友,我们就需要对某希哥的代码进行更改,重新建立关系。鉴于这种情况,就需要给某希哥找一个助理,当某希哥需要女朋友的时候,将要找女朋友的标准告诉助理,然后助理将合适的女朋友交给某希哥。 所以我们选择用如下的方式来修改当前的代码: - #import <Foundation/Foundation.h>
- #import " ZhouMouTong.h"
- @protocol Girlfriend <NSObject>
- - (void)takePicture;
- - (void)sleep;
- @end
-
- @interface Assistant : NSObject
- - (id<Girlfriend>)findGirlfriend;
- @end
-
- @implementation Assistant
- - (id<Girlfriend>)findGirlfriend{
- //助手在这个方法内为冠希哥找女朋友并返回
- }
- @end
-
- @interface ChenMouXi: NSObject
- @property(nonatomic,strong) id<Girlfriend> gf;
- @end
-
- @implementation ChenMouXi
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- //在这里,当某希哥需要女朋友的时候,由助手找一个满足要求的妹子对象
- Assistant *ass = [Assistant new];
- self.gf = [ass findGirlfriend];
- }
- return self;
- }
- @end
- #import <Foundation/Foundation.h>
- #import " ZhouMouTong.h"
- @protocol Girlfriend <NSObject>
- - (void)takePicture;
- - (void)sleep;
- @end
-
- @interface Assistant : NSObject
- - (id<Girlfriend>)findGirlfriend;
- @end
-
- @implementation Assistant
- - (id<Girlfriend>)findGirlfriend{
- //助手在这个方法内为冠希哥找女朋友并返回
- }
- @end
-
- @interface ChenMouXi: NSObject
- @property(nonatomic,strong) id<Girlfriend> gf;
- @end
-
- @implementation ChenMouXi
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- //在这里,当某希哥需要女朋友的时候,由助手找一个满足要求的妹子对象
- Assistant *ass = [Assistant new];
- self.gf = [ass findGirlfriend];
- }
- return self;
- }
- @end
复制代码
当进行如上修改之后,通过加入一个助手类,使得ChenMouXi类和女朋友类之间进行了解耦。 通过上面的例子,我们就可以理解IoC的概念了,IoC全称是什么?控制反转,分解开来看,关键有两个: 一个是控制,一个是反转。 那我们就要问了,这个控制具体指代什么? 对应上面的例子,所谓的控制,就是指为某希哥指定女朋友的控制权,在一开始的时候,这个控制权是掌握在某希哥自己手里的。 那反转呢?控制怎么就反转了? 同样,对应上面的例子,在对代码进行更改之后,我们将为某希哥指定女友的控制权进行了移交,交给了一个助手类。 控制反转,对于软件来说,就是某一协议具体实现类的选择控制权(创建对象的控制权)从调用类中移除,转交给第三方决定。 • Objective-C中IoC的具体实现 • 实现思路分析 控制反转(IoC)就是将创建对象的控制权交给第三方,所以现在要做的事情就是创建出来这个拥有控制权的第三方。 首先,这个类需要根据不同的需求在适当的时候创建合适的对象。所以创建对象是这个类最重要的功能,那么如何在程序中众多的类中精确的找到当前所需要的类来创建对应的对象呢? 如上所述,需要根据不同的需求来创建对象,那么对所需要的对象的需求描述,在OC中我们可以使用protocol。 接下来,我将描述这个第三方类的具体实现思路: 首先,在程序中并不是所有的类都需要进行IoC控制,所以我们要对需要进行IoC控制的类进行标记,这里我选择用protocol进行标记,也就是让对应的类遵守指定的协议。 在第三方类创建的时候,读取应用程序中所有的类并遍历,依次根据之前写好的标记,来判断当前类是否需要进行IoC控制,将其缓存进一个临时数组。 此时,已经有了所有需要进行IoC控制的类,但是如何通过不同的需求来定位到指定的类呢? 数组并不能进行有效的查询工作,所以需要对该数组进行再次处理,将数组内的存储的类用描述需求的协议名称做键,将实现了该协议的所有类存入数组做为该键对应的值,以键值对的形式存入字典内,方便之后进行对应的查询工作。 当以上工作全部完成之后,要做的事情,就是为第三方类,封装根据不同需求(也就是协议)创建对象的方法。当用户传入一个协议名称,或者一个协议的时候,根据用户传入的协议,在字典中找到对应的类,创建对象进行返回,就完成了IoC的基本功能。 • 具体实现步骤 • 标记协议创建 这个协议只是一个单纯的标记作用,所以无所谓功能,创建一个空的协议就好 - @protocol InjectedClassProtocol <NSObject>
-
- @end
- @protocol InjectedClassProtocol <NSObject>
-
- @end
复制代码
• 创建拥有控制权的第三方类 - @interface Injector : NSObject
-
- @end
- @interface Injector : NSObject
-
- @end
复制代码这个类是要在整个应用程序中肩负起创建对象的重任,所以这个类必然要使用单例来创建对象。 所以需要给这个类添加两个方法,来获取单例对象 - /**
- * 获取全局注入器
- *
- * @return 返回全局注入器
- */
- + (Injector *)sharedInstance;
- /**
- * 获取全局注入器
- *
- * @return 返回全局注入器
- */
- - (Injector *)sharedInstance;
- /**
- * 获取全局注入器
- *
- * @return 返回全局注入器
- */
- + (Injector *)sharedInstance;
- /**
- * 获取全局注入器
- *
- * @return 返回全局注入器
- */
- - (Injector *)sharedInstance;
复制代码
这个类主要的功能就是,根据不同的用户需求(协议),来创建指定的对象,所以需要有对应的方法来做这件事情。 - /**
- * 通过协议名称查询实现协议类的实例
- *
- * @param protocol 协议字名称
- *
- * @return 返回实现协议类的实例,如果没有找到返回 nil
- */
- - (id)instanceForProtocolName:(NSString *)protocolName;
-
- /**
- * 通过协议查询实现协议类的实例
- *
- * @param protocol 协议
- *
- * @return 返回实现协议类的实例,如果没有找到返回 nil
- */
- - (id)instanceForProtocol:(Protocol *)protocol;
-
- /**
- * 通过协议名称查询实现协议类的实例的集合
- *
- * @param protocol 协议
- *
- * @return 返回协议实现类的实例的集合
- */
- - (NSArray *)instancesForProtocolName:(NSString *)protocolName;
-
- /**
- * 通过协议查询实现协议类的实例的集合
- *
- * @param protocol 协议
- *
- * @return 返回协议实现类的实例的集合
- */
- - (NSArray *)instancesForProtocol:(Protocol *)protocol;
- /**
- * 通过协议名称查询实现协议类的实例
- *
- * @param protocol 协议字名称
- *
- * @return 返回实现协议类的实例,如果没有找到返回 nil
- */
- - (id)instanceForProtocolName:(NSString *)protocolName;
-
- /**
- * 通过协议查询实现协议类的实例
- *
- * @param protocol 协议
- *
- * @return 返回实现协议类的实例,如果没有找到返回 nil
- */
- - (id)instanceForProtocol:(Protocol *)protocol;
-
- /**
- * 通过协议名称查询实现协议类的实例的集合
- *
- * @param protocol 协议
- *
- * @return 返回协议实现类的实例的集合
- */
- - (NSArray *)instancesForProtocolName:(NSString *)protocolName;
-
- /**
- * 通过协议查询实现协议类的实例的集合
- *
- * @param protocol 协议
- *
- * @return 返回协议实现类的实例的集合
- */
- - (NSArray *)instancesForProtocol:(Protocol *)protocol;
复制代码
• 根据标记协议读取所有需要进行IoC控制的类 在前面分析实现思路的时候提到过,首先要获取程序中所有的需要进行IoC控制的类。 第一步,获取程序中所有的类(objc_getClassList函数) 第二步,遍历所有的类,判断是否需要进行IoC控制(class_conformsToProtocol函数) 第三步,将所有需要进行IoC控制的类添加到临时数组并返回该数组 - - (NSArray *)queryInjectedClasses {
- NSMutableArray* injectedClasses = [NSMutableArray array];
- int numClasses;
- Class * classes = NULL;
- classes = NULL;
- numClasses = objc_getClassList(NULL, 0);
- if (numClasses > 0 )
- {
- Protocol* iocProtocol = @protocol(InjectedClassProtocol);
-
- classes = (Class *)malloc(sizeof(Class) * numClasses);
-
- numClasses = objc_getClassList(classes, numClasses);
- for (NSInteger i = 0; i < numClasses; i++)
- {
- Class cls = classes[i];
-
- for ( Class thisClass = cls; nil != thisClass ; thisClass = class_getSuperclass( thisClass ) )
- {
- if(class_conformsToProtocol(thisClass, iocProtocol)) {
- [injectedClasses addObject:cls];
- }
- }
- }
- free(classes);
- }
- return injectedClasses;
- }
- - (NSArray *)queryInjectedClasses {
- NSMutableArray* injectedClasses = [NSMutableArray array];
- int numClasses;
- Class * classes = NULL;
- classes = NULL;
- numClasses = objc_getClassList(NULL, 0);
- if (numClasses > 0 )
- {
- Protocol* iocProtocol = @protocol(InjectedClassProtocol);
-
- classes = (Class *)malloc(sizeof(Class) * numClasses);
-
- numClasses = objc_getClassList(classes, numClasses);
- for (NSInteger i = 0; i < numClasses; i++)
- {
- Class cls = classes[i];
-
- for ( Class thisClass = cls; nil != thisClass ; thisClass = class_getSuperclass( thisClass ) )
- {
- if(class_conformsToProtocol(thisClass, iocProtocol)) {
- [injectedClasses addObject:cls];
- }
- }
- }
- free(classes);
- }
- return injectedClasses;
- }
复制代码
• 存储读取到的类 为方便之后进行对应的查询工作,对上一步数组进行再次处理,通过遍历,将数组内的存储的类用描述需求的协议名称做键,将实现了该协议的所有类存入数组做为该键对应的值,以键值对的形式存入字典内。这一过程称之为注册IoC类。 所以这里需要给类新增一个私有的字典属性。 - NSMutableDictionary* _injectedClasses;
- NSMutableDictionary* _injectedClasses;
复制代码
接下来注册类: - - (void)registerInjectedClass:(Class)cls {
- uint protocolListCount = 0;
- //获取当前遍历的类所实现的所有协议
- Protocol * const *pArrProtocols = class_copyProtocolList(cls,&protocolListCount);
- if(pArrProtocols != NULL && protocolListCount > 0) {
- for (int i = 0; i < protocolListCount; i++) {
- Protocol *protocol = *(pArrProtocols + i);
- //将类存入字典中该协议所对应的数组中
- [self bindProtocol:protocol Class:cls];
- }
- free((void *)pArrProtocols);
- }
- }
-
- - (void)bindProtocol:(Protocol *)protocol Class:(Class)cls {
- if(protocol == nil) return;
- //获取当前协议名称
- NSString* protocolString = NSStringFromProtocol(protocol);
- //如果遵守的协议是空,或者NSObject,或者是标记协议,则不做处理
- if(protocolString == nil ||
- [protocolString isEqualToString:@"NSObject"] ||
- [protocolString isEqualToString:@"IntercepterProtocol"] ||
- protocol_isEqual(protocol, @protocol(InjectedClassProtocol))) return;
-
- //查看该协议名称所对应的类数组是否存在
- NSMutableArray *_protocolClasses = _injectedClasses[protocolString];
- //若不存在,则创建新的数组
- if(_protocolClasses == nil) {
- _protocolClasses = [NSMutableArray array];
- [_injectedClasses setObject:_protocolClasses forKey:protocolString];
- }
- //如果数组中已经包含了这个类,直接返回,什么事儿都不做
- if([_protocolClasses containsObject:cls]) return;
- [_protocolClasses addObject:cls];
- }
- - (void)registerInjectedClass:(Class)cls {
- uint protocolListCount = 0;
- //获取当前遍历的类所实现的所有协议
- Protocol * const *pArrProtocols = class_copyProtocolList(cls,&protocolListCount);
- if(pArrProtocols != NULL && protocolListCount > 0) {
- for (int i = 0; i < protocolListCount; i++) {
- Protocol *protocol = *(pArrProtocols + i);
- //将类存入字典中该协议所对应的数组中
- [self bindProtocol:protocol Class:cls];
- }
- free((void *)pArrProtocols);
- }
- }
-
- - (void)bindProtocol:(Protocol *)protocol Class:(Class)cls {
- if(protocol == nil) return;
- //获取当前协议名称
- NSString* protocolString = NSStringFromProtocol(protocol);
- //如果遵守的协议是空,或者NSObject,或者是标记协议,则不做处理
- if(protocolString == nil ||
- [protocolString isEqualToString:@"NSObject"] ||
- [protocolString isEqualToString:@"IntercepterProtocol"] ||
- protocol_isEqual(protocol, @protocol(InjectedClassProtocol))) return;
-
- //查看该协议名称所对应的类数组是否存在
- NSMutableArray *_protocolClasses = _injectedClasses[protocolString];
- //若不存在,则创建新的数组
- if(_protocolClasses == nil) {
- _protocolClasses = [NSMutableArray array];
- [_injectedClasses setObject:_protocolClasses forKey:protocolString];
- }
- //如果数组中已经包含了这个类,直接返回,什么事儿都不做
- if([_protocolClasses containsObject:cls]) return;
- [_protocolClasses addObject:cls];
- }
复制代码
• 实现查询方法 • 重写构造方法 - - (instancetype)init
- {
- self = [super init];
- if (self) {
- _injectedClasses = [NSMutableDictionary dictionary];
- //查询所有需要进行IoC控制的类
- NSArray* injectedClasses = [self queryInjectedClasses];
-
- //向字典中注册需要进行IoC控制的类
- [injectedClasses enumerateObjectsUsingBlock:^(id cls, NSUInteger idx, BOOL *stop) {
- [self registerInjectedClass:cls];
- }];
- }
- return self;
- }
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- _injectedClasses = [NSMutableDictionary dictionary];
- //查询所有需要进行IoC控制的类
- NSArray* injectedClasses = [self queryInjectedClasses];
-
- //向字典中注册需要进行IoC控制的类
- [injectedClasses enumerateObjectsUsingBlock:^(id cls, NSUInteger idx, BOOL *stop) {
- [self registerInjectedClass:cls];
- }];
- }
- return self;
- }
复制代码
• 实现单例方法 - /**
- * 获取全局注入器
- *
- * @return 返回全局注入器
- */
- + (Injector *)sharedInstance {
- static dispatch_once_t onceToken;
- static Injector *injector;
- dispatch_once(&onceToken, ^{
- injector = [[[self class] alloc] init];
- });
- return injector;
- }
-
- /**
- * 获取全局注入器
- *
- * @return 返回全局注入器
- */
- - (Injector *)sharedInstance {
- return [Injector sharedInstance];
- }
- /**
- * 获取全局注入器
- *
- * @return 返回全局注入器
- */
- + (Injector *)sharedInstance {
- static dispatch_once_t onceToken;
- static Injector *injector;
- dispatch_once(&onceToken, ^{
- injector = [[[self class] alloc] init];
- });
- return injector;
- }
-
- /**
- * 获取全局注入器
- *
- * @return 返回全局注入器
- */
- - (Injector *)sharedInstance {
- return [Injector sharedInstance];
- }
复制代码
• 使用示例 创建一个SomeProtocol协议 - #import <Foundation/Foundation.h>
- @protocol SomeProtocol <NSObject>
- - (void)sayHi;
- @end
- #import <Foundation/Foundation.h>
- @protocol SomeProtocol <NSObject>
- - (void)sayHi;
- @end
复制代码
创建一个Person类 - #import <Foundation/Foundation.h>
- #import "Injector.h"
- #import "SomeThing.h"
- //使用标记协议进行标记
- @interface Person : NSObject<InjectedClassProtocol,SomeProtocol>
- @end
-
- @implementation Person
- - (void)sayHi{
- NSLog(@"Hello World");
- }
- @end
- #import <Foundation/Foundation.h>
- #import "Injector.h"
- #import "SomeThing.h"
- //使用标记协议进行标记
- @interface Person : NSObject<InjectedClassProtocol,SomeProtocol>
- @end
-
- @implementation Person
- - (void)sayHi{
- NSLog(@"Hello World");
- }
- @end
复制代码
在main.m中使用IoC的方式获取对象 - #import <Foundation/Foundation.h>
- #import "Person.h"
-
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- Injector *injector = [Injector sharedInstance];
- Person *p = [injector instanceForProtocol:@protocol(SomeProtocol)];
- [p sayHi];
- }
- return 0;
- }
- #import <Foundation/Foundation.h>
- #import "Person.h"
-
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- Injector *injector = [Injector sharedInstance];
- Person *p = [injector instanceForProtocol:@protocol(SomeProtocol)];
- [p sayHi];
- }
- return 0;
- }
复制代码
• 内容总结 本文只是简单的将IoC的模式进行了实现,但是实现仍有如下缺陷: • 父类实现的协议,子类必然也会实现,如果通过父类实现的协议来获取对象,应该是可以得获取的,但是上文未实现。 • 通过类遵守的协议所继承的协议来获取对象,应该可以获取,但是上文未实现 • 通过协议获取对象,如果系统中存在多个遵守了该协议的类,那么拿到的只会是数组中第一个类所创建出来的对象,不合理。(可以通过配置文件来解决这个问题,类似于Spring) 下期文章,将对如上所述缺陷一一进行完善,敬请关注哦~
精华推荐:
|