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)

ServiceEntrystorage 由其objectScope 生成。

internal lazy var storage: InstanceStorage = { [unowned self] in
self.objectScope.makeStorage()
}()

ObjectScopemakeStorage方法,调用了 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()
}
}

这就意味着每一个ServiceEntrystorage 由其 ObjectScopestorageFactory 属性决定。

Transient 的实现

transientstorageFactoryTransientStorage.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的更多相关文章

  1. Swinject 源码框架(二):循环依赖的解决

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

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

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

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

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

  4. Mybatis源码解析(三) —— Mapper代理类的生成

    Mybatis源码解析(三) -- Mapper代理类的生成   在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...

  5. Zookeeper 源码(三)Zookeeper 客户端源码

    Zookeeper 源码(三)Zookeeper 客户端源码 Zookeeper 客户端主要有以下几个重要的组件.客户端会话创建可以分为三个阶段:一是初始化阶段.二是会话创建阶段.三是响应处理阶段. ...

  6. 一点一点看JDK源码(三)java.util.ArrayList 前偏

    一点一点看JDK源码(三)java.util.ArrayList liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 ArrayLi ...

  7. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  8. 25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment

    25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment 25 BasicUsageEnvironment0基本使用环境基类— ...

  9. 26 BasicUsageEnvironment基本使用环境——Live555源码阅读(三)UsageEnvironment

    26 BasicUsageEnvironment基本使用环境--Live555源码阅读(三)UsageEnvironment 26 BasicUsageEnvironment基本使用环境--Live5 ...

随机推荐

  1. 2018.09.17 atcoder Tak and Cards(背包)

    传送门 背包经典题. 直接f[i][j]f[i][j]f[i][j]表示选i张牌和为j的方案数. 最后统计答案就行了. 代码: #include<bits/stdc++.h> #defin ...

  2. 山东省第七届ACM竞赛 C题 Proxy (Dijkstra算法,单源路径最短问题)

    题意:给定0-n+1个点,和m条边,让你找到一条从0到n+1的最短路,输出与0相连的结点... 析:很明显么,是Dijkstra算法,不过特殊的是要输出与0相连的边,所以我们倒着搜,也是从n+1找到0 ...

  3. 在终端上创建Java项目及编译和运行

    一:实践一次这样的操作有助于理解Tomcat/Eclipse的启动原理,包括classpath的设置,option的配置等等: 二:通过Bash终端创建一个简单的Java项目(单项目单Module,如 ...

  4. 8) pom.xml

    http://maven.apache.org/ref/3.3.3/maven-model/maven.html 执行mvn命令的时候默认文件名pom.xml 也可以通过 -f 指定 比如 mvn - ...

  5. JavaScript面向对象编程[转]

    JavaScript面向对象编程 命名空间 命名空间是一个容器,它允许开发人员在一个独特的,特定于应用程序的名称下捆绑所有的功能. 在JavaScript中,命名空间只是另一个包含方法,属性,对象的对 ...

  6. Android RelativeLayout属性介绍

    在Android开发当中,虽然有五大布局,但我推荐使用的是相对布局,Google也是推荐使用相对布局,所有对RelativeLayout布局,常用的属性做一个整理: android:layout_ma ...

  7. 曲演杂坛--使用TRY CATCH应该注意的一个小细节

    群里一个朋友遇到一个TRY CATCH的小问题,测试后发现是自己从来没有考虑的情况,写篇blog加深下印象 --============================================ ...

  8. ASP.NET Core真实管道详解[1]

    ASP.NET Core管道虽然在结构组成上显得非常简单,但是在具体实现上却涉及到太多的对象,所以我们在 <ASP.NET Core管道深度剖析[共4篇]> 中围绕着一个经过极度简化的模拟 ...

  9. 用注册表禁止windows添加新用户

    运行 regedt32.exe 打开你的注册表,里面有一个目录树:打开其中目录 HKEY_LOCAL_MACHINE再打开其中目录 SAM再打开其中目录 SAM再打开其中目录 Domains再打开其中 ...

  10. 创建/读取/删除Session对象

    //创建Session对象 Session["userName"] = "顾德博";//保存,这里可以存储任意类型的数据,包括对象.集合等 Session.Ti ...