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. Memocache

    http://blog.csdn.net/zhoufoxcn/article/details/6282099 http://blog.csdn.net/dinglang_2009/article/de ...

  2. winsock select 学习代码(2)

    之前文章的改进版 服务器仅仅接受客户端发送的字符串并显示 客户端可以调节发送数目 但是不能超过64 // SelectServer.cpp : 定义控制台应用程序的入口点. // #include & ...

  3. spring+hibernate 整合异常 Class 'org.apache.commons.dbcp.BasicDataSource' not found

    解决方法 添加 commons-dbcp.jar和commons-pool.jar包

  4. scanf和fscanf读取文件

    这篇是针对推箱子游戏而写的,某个时候在学C语言,最近转到windows设计,不知道是否有同样的感受,后面的东西学了,前面的就有点生疏了.其实,我的理解是,注意力转移了,当集中于当前问题的时候就会忽略以 ...

  5. (转) MVC身份验证及权限管理-1

    转自:http://blog.csdn.net/kenshincui/article/details/5559508 MVC自带的ActionFilter 在Asp.Net WebForm的中要做到身 ...

  6. Java中JNI的使用详解第三篇:JNIEnv类型中方法的使用

    转自: http://blog.csdn.net/jiangwei0910410003/article/details/17466369 上一篇说道JNIEnv中的方法的用法,这一篇我们就来通过例子来 ...

  7. codeforces 702C Cellular Network 2016-10-15 18:19 104人阅读 评论(0) 收藏

    C. Cellular Network time limit per test 3 seconds memory limit per test 256 megabytes input standard ...

  8. MySQL Route负载均衡与读写分离Docker环境使用

    Docker环境描述 主机名 部署服务 备注 MySQL Route MySQL Route 部署在宿主机上的MySQL Route服务 MySQL Master1 MySQL 5.7.16 Dock ...

  9. ASP.NET MVC 中单独的JS文件中获取Controller中设定的值

    1,在Controller中的Action 中将指定值写上.       //       // GET: /Home/       public ActionResult Index()       ...

  10. .Net Core 跨平台应用使用串口、串口通信 ,可能出现的问题、更简洁的实现方法

    前些天在学习在 .NET Core下,跨平台使用串口通讯,有一篇文章说到在Linux/物联网下,实现通讯. 主要问题出现在以下两个类库 SerialPortStream flyfire.CustomS ...