条件:闲来有意,偶作文章。

摘要:依靠注入(dependency injection,缩写为 DI)是一种软件设计形式,也是完成操控反转的其间一种技术。这种形式能让一个物件接纳它所依靠的其他物件。“依靠”是指接纳方所需的目标。“注入”是指将“依靠”传递给接纳方的进程。在“注入”之后,接纳方才会调用该“依靠”。

  • 第一种:运用协议结耦,进行依靠注入。容器Container进行类/协议注入,讲述以Swinject为例。

(一)、类/协议注册+相关目标实例的通用办法进口:

@discardableResult
public func register<Service>(
    _ serviceType: Service.Type,
    name: String? = nil,
    factory: @escaping(Resolver) -> Service
) -> ServiceEntry<Service> {
    return _register(serviceType, factory: factory, name: name)
}

1.1,在此办法内部完成,维护了一个以ServiceKey类型为key,ServiceEntry类型为value的字典services,ServiceEntry实例持有了上述办法的factory-closure闭包(此closure闭包一般用于创建类实例目标)。

1.2 在register之后能够经过设置.inObjectScope(##ObjectScope##)的办法,指定此次注册的(协议)类相关的实例目标具有什么level的场景。关于level的设定,是在ServiceEntry内部完成,代码如下:

internal lazy var storage: InstanceStorage = { [unowned self] in
    self.objectScope.makeStorage() 
}() 
/// Will invoke and return the result of `storageFactory` closure provided during initialisation.
public func makeStorage() -> InstanceStorage {
    if let parent = parent {
        return CompositeStorage([storageFactory(), parent.makeStorage()])
    } else {
        return storageFactory()
    }
}
//这儿storageFactory是一个closure,对应level明细中的ObjectScope(storageFactory:传入的init办法
private var storageFactory: () -> InstanceStorage

level明细:

extension ObjectScope {
  /// A new instance is always created by the ``Container`` when a type is resolved.
  /// The instance is not shared.
  public static let transient = ObjectScope(storageFactory: TransientStorage.init, description: "transient")
  /// Instances are shared only when an object graph is being created,
  /// otherwise a new instance is created by the ``Container``. This is the default scope.
  public static let graph = ObjectScope(storageFactory: GraphStorage.init, description: "graph")
  /// An instance provided by the ``Container`` is shared within the ``Container`` and its child `Containers`.
  public static let container = ObjectScope(storageFactory: PermanentStorage.init, description: "container")
  /// An instance provided by the ``Container`` is shared within the ``Container`` and its child ``Container``s
  /// as long as there are strong references to given instance. Otherwise new instance is created
  /// when resolving the type.
  public static let weak = ObjectScope(storageFactory: WeakStorage.init, description: "weak", parent: ObjectScope.graph)
}

(二)、经过类协议取实例目标。

整个进程与上述照应,树立ServiceKey目标作为key,经过entry = getEntry(for: key)操作services字典取出entry目标。接着进行以下操作

func resolve<Service, Factory>(entry: ServiceEntryProtocol, invoker: (Factory) -> Any) -> Service? {
    incrementResolutionDepth()
    //defer是在整个办法域履行结束前履行,例如在此便是在return后的办法履行完后,才会履行defer内的办法。此关键字有点文章,在此不赘述,结束会放一个case。
    defer { decrementResolutionDepth() }
    guard let currentObjectGraph = currentObjectGraph else {
        fatalError("If accessing container from multiple threads, make sure to use a synchronized resolver.")
    }
    //判别是否现已存在persistedInstance,存在即从instances取出
    if let persistedInstance = persistedInstance(Service.self, from: entry, in: currentObjectGraph) {
     //取实例源码:entry.storage.instance(inGraph: graph) as? Service
        return persistedInstance
    }
    //这儿照应上面的register办法中的ServiceEntry持有的factory-closure闭包。
    let resolvedInstance = invoker(entry.factory as! Factory)
    if let persistedInstance = persistedInstance(Service.self, from: entry, in: currentObjectGraph) {
        // An instance for the key might be added by the factory invocation.
        return persistedInstance
    }
    //内部均触及对InstanceStorage.instance赋值操作,不同level操作逻辑不同
    entry.storage.setInstance(resolvedInstance as Any, inGraph: currentObjectGraph)
   //code...
    return resolvedInstance as? Service
}
  • 第二种:监听者遵从协议 + 【+load】办法注入监听者 + AssicationManager存储类办理。

在原理上实质也是用了Manager办理类的概念,一起与VC进行了1对1的owner持有者仅有联系绑定。在manager办理类内部进行监听者类的搜集,调集办法为NSMutableArray<Class<InjectListenerProtocol>>InjectListenerProtocol是想实施监听的类所恪守的Base协议。Base协议有多个子协议,根据不同通用功用的vc界说不同的继承协议。例如:UIViewController,UINavigatonContronller,UITabBarViewController,以及乃至UIWindonw级别的只需需求被监听且具有清晰的生命周期步骤均可运用。

完成进程简述:假如我们完成一个Manager类,需求监听TestVC的willAppear和didDisapper时机。 1,在Manager类做以下:

(1),+load办法内部进行注入[TestVC injectListenerClass:self]

(2),恪守InjectListenerProtocol.Type类的协议;

(3),完成协议的与willAppear与didDisapper对应的办法;

2,在TestVC类内需求根据不同的生命周期的办法内部进行监听者的遍历,经过遍历对监听者恪守的协议的办法进行回调。

关于上述的进程,读者或许会有一个疑问便是:监听者和被监听者的是怎么被绑定到一起的。假如完成大胆些,那这些应该够用了。

场景:根据生命周期的监听,能够直接在单例内部进行操作,经过协议内监听办法回调触发时机

  • 第三种:事务类恪守协议 + 路由类树立协议调集类 + 路由类注册顾客。

以自己项目为例,内容会尽或许的以文字表述,但会贴出关键技术代码,因为触及项目技术保密,所以会写伪代码替代。(此技术不接受讨论,抱歉)

完成原理简述分三部分:

1,+load注册顾客。

+load办法的加载时机是在loadImages阶段,大致如下:

void _objc_init(void)
{
  static bool initialized = false;
  if (initialized) return;
  initialized = true;
  // fixme defer initialization until an objc-using image is found?
  environ_init();
  tls_init();
  static_init();
  runtime_init();
  exception_init();
#if __OBJC2__
  cache_t::init();
#endif
  _imp_implementationWithBlock_init();
    /**
    map_images阶段会进行类和分类的加载;
    loadImages阶段会调用load办法
    */
  _dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
  didCallDyldNotifyRegister = **true**;
#endif
}

项目中用自界说的链表结构办理顾客(现已作为目标,加了一层封装),链表Node节点中会持有顾客的id标识,其间也触及到顾客优先级的办理以及链表的增加、移除、节点保存等;

2,创建路由类检测协议恪守者并判别是否完成协议制定的办法,树立Map调集以满意条件的类为value,以类有关的事务type为key。(此事务type表示将要跳转的场景,此处是枚举概念)。

第一步找到一切注册的类

*Talking is cheap,show the codes*
/**
* Creates and returns a list of pointers to all registered class definitions.
* @param outCount An integer pointer used to store the number of classes returned by
* this function in the list. It can be \c nil.
* @return A nil terminated array of classes. It must be freed with \c free().
* @see objc_getClassList
*/
OBJC_EXPORT Class _Nonnull * _Nullable
objc_copyClassList(unsigned int * _Nullable outCount)

第二步:判别类是否恪守指定的协议

/**
* Returns a Boolean value that indicates whether a class conforms to a given protocol.
* @param cls The class you want to inspect.
* @param protocol A protocol.
* @return YES if cls conforms to protocol, otherwise NO.
* @note You should usually use NSObject's conformsToProtocol: method instead of this function.
*/
OBJC_EXPORT BOOL
class_conformsToProtocol(Class **_Nullable** cls, Protocol * **_Nullable** protocol)

第三步:判别类是否完成了指定协议的办法

OBJC_EXPORT Method _Nullable
class_getClassMethod(Class **_Nullable** cls, **SEL** **_Nonnull** name)

3,路由类PushManager触发Event,并带着事务Map信息(包括type值:数字)。map信息被解析成真正的事务Event,然后被传递给总线BusSystem,之后便等候被唤醒触发事务事情。此间还触及同步、异步、Timer事情的分发识别的逻辑,因和主题无关不赘述。

ps:此场景有结合runloop+线程保活技术轮询,树立BusSystem总线观念,功用分发。其功用相当于守时从起点接送乘客,不定站点下车。

  • 第四种:经过自界说section端的办法动态注册类与协议,形成映射。

此处重点解析BeeHive,一起会链接到多线程异步完成的源码。 (在模块进口类完成中 运用 BH_EXPORT_MODULE () 宏声明该类为模块进口完成类。) BeeHive还存在一种静态写入:经过plist办法直接完成,注册契合 BHModuleProtocol 协议模块类。

动态注册进程原理简述

(一)、根据类与协议的注册演示: @BeeHiveService(HomeServiceProtocol,BHViewController)

#define BeeHiveDATA(sectname) __attribute((used, section("__DATA,"#sectname" ")))
#define BeeHiveMod(name) \
class BeeHive; char * k##name##_mod BeeHiveDATA(BeehiveMods) = ""#name"";
#define BeeHiveService(servicename,impl) \
class BeeHive; char * k##servicename##_service BeeHiveDATA(BeehiveServices) = "{ \""#servicename"\" : \""#impl"\"}";

(二)、根据事情与类实例目标的相关注册演示:BH_EXPORT_MODULE(YES)。官方文档解说:假如此参数设置为YES时,则会在启动之后第一屏内容展现之前异步履行模块的初始化,能够优化启动时时刻消耗。

#define BH_EXPORT_MODULE(isAsync) \
+ (void)load { [BeeHive registerDynamicModule:[self class]]; } \
-(BOOL)async { return [[NSString stringWithUTF8String:#isAsync] boolValue];}

(三)、关键完成源码展现

__attribute__ ((constructor))
void initProphet() {
    /**
    The following functions allow you to install callbacks which will be called 
* by dyld whenever an image is loaded or unloaded. During a call to _dyld_register_func_for_add_image()
* the callback func is called for every existing image. Later, it is called as each new image
* is loaded and bound (but initializers not yet run).
    */ 大意是在dyld安装或卸载镜像时,下面的函数设置的回调会被调用。在调用此函数期间,回调会被每一个现已存在的镜像文件调用。此刻初始化程序还未运转完,
  _dyld_register_func_for_add_image(dyld_callback);
}
static void dyld_callback(const struct mach_header *mhp, intptr_t vmaddr_slide) {
NSArray *mods = BHReadConfiguration(BeehiveModSectName, mhp);
for (NSString *modName in mods) {
    //code... =》 转化类
    // 此办法内部主要是事情与类(触及到创建实例目标)的相关事情,
    [[BHModuleManager sharedManager] registerDynamicModule:cls];
}
//register services
NSArray<NSString *> *services = BHReadConfiguration(BeehiveServiceSectName,mhp);
for (NSString *map in services) {
  //code...=》map键值对解析成独立元素
     //办法内部:协议与类的分别转化成key-value,并加入字典self.allServicesDict。
    [[BHServiceManager sharedManager] registerService:NSProtocolFromString(protocol) implClass:NSClassFromString(clsName)];
}

读取MachO文件的指定section段名下的信息,并进行解析成字符串。

 NSArray<NSString *>* BHReadConfiguration(char *sectionName,const struct mach_header *mhp)
{
  NSMutableArray *configs = [NSMutableArray array];
  unsigned long size = 0;
  const struct mach_header_64 *mhp64 = (const struct mach_header_64 *)mhp;
  uintptr_t *memory = (uintptr_t*)getsectiondata(mhp64, SEG_DATA, sectionName, &size);
  unsigned long counter = size/sizeof(void*);
  for(int idx = 0; idx < counter; ++idx){
    char *string = (char*)memory[idx];
    NSString *str = [NSString stringWithUTF8String:string];
    if(!str)continue;
    BHLog(@"config = %@", str);
    if(str) [configs addObject:str];
  }
  return configs;
}

读取进程展现:

iOS依赖注入:技术原理+应用场景,四种方式+源码解读

BeeHiveDemo build之后的macho文件查看如下:

iOS依赖注入:技术原理+应用场景,四种方式+源码解读

(四)相关苹果官方源码:在深入了解BeeHive源码后,意识到苹果官方源码中在对线程的内部完成也运用了注册section段的技术。

void dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
    codes...
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu, dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
    codes...
    return dx_push(dqu._dq, dc, qos);
}
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
//查找dq_push 
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_main, lane,
    .do_type    = DISPATCH_QUEUE_MAIN_TYPE,
    .do_dispose   = _dispatch_lane_dispose,
    .do_debug    = _dispatch_queue_debug,
    .do_invoke   = _dispatch_lane_invoke,
    .dq_activate  = _dispatch_queue_no_activate,
    .dq_wakeup   = _dispatch_main_queue_wakeup,
     ///句柄
    .dq_push    = _dispatch_main_queue_push, 
);
#define DISPATCH_VTABLE_SUBCLASS_INSTANCE(name, ctype, ...) \
OS_OBJECT_VTABLE_SUBCLASS_INSTANCE(dispatch_##name, dispatch_##ctype, \
_dispatch_xref_dispose, _dispatch_dispose, \
.do_kind = #name, __VA_ARGS__)
#if OS_OBJECT_HAVE_OBJC2
#define OS_OBJECT_VTABLE_SUBCLASS_INSTANCE(name, ctype, xdispose, dispose, ...) \

//此处界说在名为__objc_data的__DATA段
__attribute__((section("__DATA,__objc_data"), used)) \
const struct ctype##_extra_vtable_s \
OS_OBJECT_EXTRA_VTABLE_SYMBOL(name) = { __VA_ARGS__ }
codes...
#else
  • 结束赘述:

1,Swift-defer关键字演示:

var a = 0
func test() -> Int{
    a = 2
    defer {
        a = -1
    }
    return test1()
}
func test1() -> Int {
    a = 6
    return 2
}

2,OC中,分懒加载类和非懒加载类,完成了+load办法的为非懒加载。假如大量频频的运用+load办法,会不会触及时刻优化,因其间也触及到主类与分类的attached。

3,代码中__attribute__用于指定编译特性:包括:Function Attributes、Variable Attributes、Type Attributes。在这儿明显是作为润饰变量的 variable attributes。unused表示变量未必会被运用,section用于指定变量所保存到的数据段,参数为数据段名。