可能存在循环依赖,比如 Parent 强制有 ChildChild 弱持有 Parent
具体实现如下。Parent 初始化时,必须传入 Child,而 Child 初始化不必传入 Parent

protocol ParentProtocol: AnyObject { }
protocol ChildProtocol: AnyObject { } class Parent: ParentProtocol {
let child: ChildProtocol? init(child: ChildProtocol?) {
self.child = child
}
} class Child: ChildProtocol {
weak var parent: ParentProtocol?
}

具体调用方式如下:

let container = Container()
container.register(ParentProtocol.self) { r in
let child = r.resolve(ChildProtocol.self)!
let parent = Parent(child: child)
return parent
}
container.register(ChildProtocol.self) { _ in Child() }
.initCompleted { r, c in
let child = c as! Child
child.parent = r.resolve(ParentProtocol.self)
} let parent = container.resolve(ParentProtocol.self)

注意把 Child 的属性赋值放在了 initCompleted 里。

结合代码看时序

// 如果已经存在实例了,就直接返回。可以避免了无限循环
if let persistedInstance = entry.storage.instance(inGraph: currentObjectGraph), let persistedService = persistedInstance as? Service {
return persistedService
} //生成一个新的实例
let resolvedInstance = invoker(entry.factory as! Factory) // 最开始没有实例,可能在调用invoker后,可能已经有新的实例在entry 的 strage 里了,比如上面的 P1。
if let persistedInstance = entry.storage.instance(inGraph: currentObjectGraph), let persistedService = persistedInstance as? Service {
// An instance for the key might be added by the factory invocation.
return persistedService
}
//把生成的实例保存起来
entry.storage.setInstance(resolvedInstance as Any, inGraph: currentObjectGraph) // 初始化完成以后,调用 initComplete
if let completed = entry.initCompleted as? (Resolver, Any) -> Void,
let resolvedInstance = resolvedInstance as? Service { completed(self, resolvedInstance)
} return resolvedInstance as? Service

依赖的类型

上面的例子中,ChildParent 的初始化参数,ParentChild 的属性。这种依赖称为 Initializer/Property Dependencies.
也可能 ChildParent 分别是其依赖的属性,这种依赖称为 Initializer/Property Dependencies。
也可能 ChildParent 分别是其依赖的初始化参数。这种依赖,使用 Swinject,暂时无法解决无限循环的问题。

循环依赖可能导致的问题

某一个factory方法可能会被执行两次。比如上面例子中,Parent 对应的 factory 方法被执行了两次。
可能导致一些副作用,比如更加耗时。
一种解决方案是把依赖都放进在initCompleted 闭包里,这也意味着不能使用 Initializer/Property Dependencies

container.register(ParentProtocol.self) { _ in Parent()
}.initCompleted { (r, p) in
let parent = p as! Parent
parent.child = r.resolve(ChildProtocol.self)!
}
container.register(ChildProtocol.self) { _ in Child()}
.initCompleted { r, c in
let child = c as! Child
child.parent = r.resolve(ParentProtocol.self)
}

源码中和循环依赖有关的变量

  • resolutionDepth
    每次调用resolve自增。如果有循环依赖,会调用多次。
  • maxResolutionDepth
    用来探测无限循环
  • currentObjectGraph
    标记某次生成实例的过程
  • GraphStorage
    在某次解决循环依赖过程中,生成的实例都存储在这里。

Swinject 源码框架(二):循环依赖的解决的更多相关文章

  1. Spring源码分析之循环依赖及解决方案

    Spring源码分析之循环依赖及解决方案 往期文章: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostPro ...

  2. Spring源码-IOC部分-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  3. Spring源码--debug分析循环依赖--构造器注入

    目的:源码调试构造器注入,看看是怎么报错的. spring:5.2.3 jdk:1.8 一.准备 首先准备两个循环依赖的类:userService和roleServic <bean id=&qu ...

  4. Swinject 源码框架(三):Object Scopes

    Object Scopes 指定了生成的实例在系统中是如何被共享的. 如何指定 scope container.register(Animal.self) { _ in Cat() } .inObje ...

  5. Swinject 源码框架(一):基本原理

     核心是 Container类.它提供了两类方法,register 和 resolve. 为了找到在 resolve 时,能够找到对应的方法,内部维护了一个叫做services 的字典.key 是根 ...

  6. Spring源码-IOC部分-Spring是如何解决Bean循环依赖的【6】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  7. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  8. 框架-springmvc源码分析(二)

    框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...

  9. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

随机推荐

  1. js replace 用法

    /g  表示全部 global 在很多项目中,我们经常需要使用JS,在页面前面对前台的某些元素做做修改,js 的replace()方法就必不可少. 经常使用"ABCABCabc". ...

  2. sqlldr 笔记

    表结构 CREATE table sqlloader_test ( f1 char(20), f2 char(20), f3 number(16), f4 date ); 数据文件data.csv 1 ...

  3. 使用Application Center Test (ACT)来做压力测试 【转】

    在我们完成了基于SPS2003的开发,实现了我们的具体应用以后,我们是不是就可以直接请用户来使用了呢?如果我这么做,那么有经验的开发人员一定会对此嗤之以鼻:居然连压力测试也不做!真是不想活了…… 呵呵 ...

  4. jQuery获得元素位置offset()和position()的区别

    jQuery获得元素位置offset()和position()的区别 jQuery 中有两个获取元素位置的方法offset()和position(),这两个方法之间有什么异同 offset(): 获取 ...

  5. java中配置JPA方法

    JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中 使用JPA进行保存对象时,可以用对象来接收,例 ...

  6. 2018.10.14 NOIP训练 猜数游戏(决策单调性优化dp)

    传送门 一道神奇的dp题. 这题的决策单调性优化跟普通的不同. 首先发现这道题只跟r−lr-lr−l有关. 然后定义状态f[i][j]f[i][j]f[i][j]表示猜范围为[L,L+i−1][L,L ...

  7. 2018.09.16 loj#10243. 移棋子游戏(博弈论)

    传送门 题目中已经给好了sg图,直接在上面跑出sg函数即可. 最后看给定点的sg值异或和是否等于0就判好了. 代码: #include<bits/stdc++.h> #define N 2 ...

  8. 处理jQuery选择器中的特殊符号,如(、#等

    前几天解决一个外网问题,客服反馈页面数据加载不出来,首先看一下服务端日志也没报错异常,自己测试了一下,在chrome的Console发现有js报错,原来是js报错导致的数据加载不出来. 调试了一番,发 ...

  9. 对call() apply() 方法的简单理解

    真的是非常简单的理解,我知道的并不多,在网上查找了很多的资料,还是只能了解一点皮毛,下面来整理出来,方便以后深入的去学习,也是对目前知道的知识点的巩固. 整理一些网上的经典解答: 1.一句话区分cal ...

  10. UVa 11178 Morley's Theorem (几何问题)

    题意:给定三角形的三个点,让你求它每个角的三等分线所交的顶点. 析:根据自己的以前的数学知识,应该很容易想到思想,比如D点,就是应该求直线BD和CD的交点, 以前还得自己算,现在计算机帮你算,更方便, ...