Swinject 源码框架(三):Object Scopes
Object Scopes 指定了生成的实例在系统中是如何被共享的。
如何指定 scope
container.register(Animal.self) { _ in Cat() }
.inObjectScope(.container)
例子如上,每次 register
方法,都会返回 ServiceEntry
实例,然后调用其 inObjectScope
方法,会设置其 objectScope
。
scope 的种类
- Transient
每次调用resolve,都会生成新的实例。 - Graph (默认值)
每次手动调用 resolve,都会生成新的实例。
但是在其 factory 方法中生成的实例,是共享的,即如果已经生成过了,就不会重新生成了。
用于解决循环依赖 - Container
被这个 Container 及其子 Container 共享。可以理解为单例。 - Weak
和 Container 类似,但是Container 并不持有它。如果没有其他引用,这个实例会被销毁,下次重新生成。
Scope 的实现
每一个 ServiceEntry
都有持有一个 InstanceStorage
,保存着已经生成的 instance
。
每一次调用 resolve
,都会先从其storage
中获取 instance
。如果获取不到,才会去生成新的实例。
if let persistedInstance = entry.storage.instance(inGraph: currentObjectGraph), let persistedService = persistedInstance as? Service {
return persistedService
}
let resolvedInstance = invoker(entry.factory as! Factory)
ServiceEntry
的 storage
由其objectScope
生成。
internal lazy var storage: InstanceStorage = { [unowned self] in
self.objectScope.makeStorage()
}()
ObjectScope
的 makeStorage
方法,调用了 storageFactory
这个闭包属性。
/// 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()
}
}
这就意味着每一个ServiceEntry
的 storage
由其 ObjectScope
的 storageFactory
属性决定。
Transient 的实现
transient
的 storageFactory
是 TransientStorage.init
。
public static let transient = ObjectScope(storageFactory: TransientStorage.init, description: "transient")
而 TransientStorage
每次返回的 instance
都是nil,因此每次都生成一个新的实例。
/// Does not persist stored instance.
public final class TransientStorage: InstanceStorage {
public var instance: Any? {
get { return nil }
set {}
}
public init() {}
}
Container Scope 的实现
extension InstanceStorage {
public func graphResolutionCompleted() {}
public func instance(inGraph _: GraphIdentifier) -> Any? { return instance }
public func setInstance(_ instance: Any?, inGraph _: GraphIdentifier) { self.instance = instance }
}
/// Persists stored instance until it is explicitly discarded.
public final class PermanentStorage: InstanceStorage {
public var instance: Any?
public init() {}
}
可见,每次生成一个实例,就会设置为其 instance
属性,并且强持有。
所以只要生成过一次,就不会被释放,也不会重新生成。
Weak Scope 的实现
/// Does not persist value types.
/// Persists reference types as long as there are strong references to given instance.
public final class WeakStorage: InstanceStorage {
private var _instance = Weak<Any>()
public var instance: Any? {
get { return _instance.value }
set { _instance.value = newValue }
}
public init () {}
}
和 Container 类似,但是这里的instance被包了一层,即一个强对象中,有一个 weak 的属性。真正设置的是其 weak 的属性。
所以这个 storage 并不强持有 instance。因此外部的引用都消失后,instance
的 get 方法会返回 nil。
private class Weak<Wrapped> {
private weak var object: AnyObject?
var value: Wrapped? {
get {
guard let object = object else { return nil }
return object as? Wrapped
}
set { object = newValue as AnyObject? }
}
}
Graph Scope 的实现
/// Persists storage during the resolution of the object graph
public final class GraphStorage: InstanceStorage {
private var instances = [GraphIdentifier: Weak<Any>]()
public var instance: Any?
public init() {}
public func graphResolutionCompleted() {
instance = nil
}
public func instance(inGraph graph: GraphIdentifier) -> Any? {
return instances[graph]?.value
}
public func setInstance(_ instance: Any?, inGraph graph: GraphIdentifier) {
self.instance = instance
if instances[graph] == nil { instances[graph] = Weak<Any>() }
instances[graph]?.value = instance
}
}
和 Transient
类似,不过在某一次 resolve 之后,会调用 graphResolutionCompleted
,把 instance 清空。
// in Container.swift
fileprivate func decrementResolutionDepth() {
assert(resolutionDepth > 0, "The depth cannot be negative.")
resolutionDepth -= 1
if resolutionDepth == 0 {
services.values.forEach { $0.storage.graphResolutionCompleted() }
self.currentObjectGraph = nil
}
}
Swinject 源码框架(三):Object Scopes的更多相关文章
- Swinject 源码框架(二):循环依赖的解决
可能存在循环依赖,比如 Parent 强制有 Child, Child 弱持有 Parent. 具体实现如下.Parent 初始化时,必须传入 Child,而 Child 初始化不必传入 Parent ...
- Swinject 源码框架(一):基本原理
 核心是 Container类.它提供了两类方法,register 和 resolve. 为了找到在 resolve 时,能够找到对应的方法,内部维护了一个叫做services 的字典.key 是根 ...
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- Mybatis源码解析(三) —— Mapper代理类的生成
Mybatis源码解析(三) -- Mapper代理类的生成 在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...
- Zookeeper 源码(三)Zookeeper 客户端源码
Zookeeper 源码(三)Zookeeper 客户端源码 Zookeeper 客户端主要有以下几个重要的组件.客户端会话创建可以分为三个阶段:一是初始化阶段.二是会话创建阶段.三是响应处理阶段. ...
- 一点一点看JDK源码(三)java.util.ArrayList 前偏
一点一点看JDK源码(三)java.util.ArrayList liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 ArrayLi ...
- tomcat源码分析(三)一次http请求的旅行-从Socket说起
p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...
- 25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment
25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment 25 BasicUsageEnvironment0基本使用环境基类— ...
- 26 BasicUsageEnvironment基本使用环境——Live555源码阅读(三)UsageEnvironment
26 BasicUsageEnvironment基本使用环境--Live555源码阅读(三)UsageEnvironment 26 BasicUsageEnvironment基本使用环境--Live5 ...
随机推荐
- stl学习记录(1)
Effective STL 中文版学习记录 条款4 判断容器是否为空 使用empty而不是size().size()操作在实现上不是一个时间常数操作条款5 尽量使用区间成员函数代替它们的单元素兄弟.S ...
- client.HConnectionManager$HConnectionImplementation: Can't get connection to ZooKeeper: KeeperErrorCode = ConnectionLoss for /hbase
解决方法:hbase 未成功启动 1.关闭防火墙:service iptables stop 2.start-hbase.sh
- window.load 和$(document).ready() 、window.load和body onload区别
1.执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行. $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕.2.编写个数不同 w ...
- 用org.mybatis.generator 生成代码
1:引入pom 2:增加生成配置xml: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ...
- 程序员面试50题(1)—查找最小的k个元素[算法]
题目:输入n个整数,输出其中最小的k个.例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 分析:这道题最简单的思路莫过于把输入的n个整数排序,这样排在最前面的k个数 ...
- C#和MatLab的混合编程(充分利用二者的优势)
C#和MatLab的混合编程,充分利用了winform的直观显示和matlab的强大计算能力.在此以一个小例子的形式给大家讲述一下二者混合编程的实现. 一.软件的配置说明 C#版本:VS2010:Ma ...
- nexus 私服跑一跑流程
尝试建立新项目上传,分享 D:\dev\workspace\helloworld>tree /F 卷 DATAPART 的文件夹 PATH 列表 卷序列号为 2C22-0A1A D:. │ po ...
- 使用OpenCV进行相机标定
1. 使用OpenCV进行标定 相机已经有很长一段历史了.但是,伴随着20世纪后期的廉价针孔照相机的问世,它们已经变成我们日常生活的一种常见的存在.不幸的是,这种廉价是由代价的:显著的变形.幸运的是, ...
- 页面中的删除确认(ajax)、输入框中确认信息是否可用(ajax)的jquery代码
1.页面中的删除确认(ajax) <%@ page language="java" contentType="text/html; charset=UTF-8&qu ...
- [PHP-Debug] 使用 php -l 调试 PHP 错误遇到的坑
有时候,因为系统代码的增加,造成很多文件的相互关联,又或者某些第三接口(微信等),你必须要在线上调试. 线上环境,我们都是设置 “ini_set('display_errors' , false)” ...