Swinject 源码框架(二):循环依赖的解决
可能存在循环依赖,比如 Parent 强制有 Child, Child 弱持有 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
依赖的类型
上面的例子中,Child 是 Parent 的初始化参数,Parent 是 Child 的属性。这种依赖称为 Initializer/Property Dependencies.
也可能 Child 和 Parent 分别是其依赖的属性,这种依赖称为 Initializer/Property Dependencies。
也可能 Child 和 Parent 分别是其依赖的初始化参数。这种依赖,使用 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 源码框架(二):循环依赖的解决的更多相关文章
- Spring源码分析之循环依赖及解决方案
Spring源码分析之循环依赖及解决方案 往期文章: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostPro ...
- Spring源码-IOC部分-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
- Spring源码--debug分析循环依赖--构造器注入
目的:源码调试构造器注入,看看是怎么报错的. spring:5.2.3 jdk:1.8 一.准备 首先准备两个循环依赖的类:userService和roleServic <bean id=&qu ...
- Swinject 源码框架(三):Object Scopes
Object Scopes 指定了生成的实例在系统中是如何被共享的. 如何指定 scope container.register(Animal.self) { _ in Cat() } .inObje ...
- Swinject 源码框架(一):基本原理
 核心是 Container类.它提供了两类方法,register 和 resolve. 为了找到在 resolve 时,能够找到对应的方法,内部维护了一个叫做services 的字典.key 是根 ...
- Spring源码-IOC部分-Spring是如何解决Bean循环依赖的【6】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- 框架-springmvc源码分析(二)
框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
随机推荐
- hdu-2844(完全背包+二进制优化模板)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2844 思路:问1-m能的得到的硬币的值,所以dp[i]==i即可. #include<iostr ...
- asp.net 中长尾链接实现推送 -- comet
一般需求推送服务时,都会去第三方拿推送组件,如”极光“,”百度“,”小米"什么的,自己用.net实现推送服务端需要面对很多问题,比如C10K,但是企业内部使用往往用不了10K的链接,有个1K ...
- 深度linux没有ll等命令的解决办法
编辑~/.bashrc, 添加alias 如下 vim ~/.bashrc 设置别名. 添加如下行 alias ll='ls -alF' alias la='ls -A' alias vi='vim' ...
- 修改Python IDLE代码配色及语法高亮主题
初学Python,想必大家拿来练习最多的IDE就是Python自带的IDLE了,但是默认的代码配色及语法高亮主题确实很不适应,所以我们需要做个小小的美化,比如像下面这样我做的美化配置: HOW TO ...
- POP介绍与使用实践(快速上手动画)[转]
前言 动画在APP开发过程中 大家多多少少都会接触到 而且随着ios7的扁平化风格启用之后 越来越多的APP开始尝试加入各种绚丽的动画交互效果以增加APP的用户体验(当然 还是以国外的APP居多) 有 ...
- hdu 4983 欧拉函数
http://acm.hdu.edu.cn/showproblem.php?pid=4983 求有多少对元组满足题目中的公式. 对于K=1的情况,等价于gcd(A, N) * gcd(B, N) = ...
- spring案列——annotation配置
一.需要的jar包 spring.jar commons-loggin.jar commons-loggin.jar commons-annotation.jar 二.项目结构 三.entity pa ...
- OpenCV中GPU函数
The OpenCV GPU module is a set of classes and functions to utilize GPU computational capabilities. I ...
- 阉割版BBBlack安装Debian
开门见山,直入主题 咸鱼入手3块阉割ARM板,经过快递近6天运输到手,不过价格便宜 东西下面这样的(借了咸鱼的图): 发现这块板是阉割版的国外beagleboard.org型号为BeagleBone ...
- 修改jenkins启动的默认用户
# 背景 通过yum命令安装的jenkins,通过service jenkins去启动jenkins的话,默认的用户是jenkins,但jenkins这个用户是无法通过su切换过去的 ,在某些环节可能 ...