为什么注册 Swinject 模型类时没有使用“.inObjectScope(.Container)”生成单例?

Jos*_*Mum 2 singleton ios swift swinject

这个问题是针对对Swinject for Swift有丰富经验的人。

我将展示有问题的代码,我的问题在底部。

代码有点多,抱歉。

这是MySwinjectStoryboard.swift注册:

import Swinject

extension SwinjectStoryboard
{
    class func setup ()
    {
        defaultContainer.register( Stopwatch.self )
        {
            responder in Stopwatch( 
                signals: responder.resolve( SignalsService.self )! 
            )
        }

        defaultContainer.register( SignalsService.self ) 
        { 
            _ in SignalsService() 
        }.inObjectScope( .Container )

        defaultContainer.register( ImageService.self )
        {
            responder in ImageService(
                signals: responder.resolve( SignalsService.self )!
                , stopwatch: responder.resolve( Stopwatch.self )!
            )
        }.inObjectScope( .Container )

        defaultContainer.registerForStoryboard( StartUpViewController.self )
        {
            resolvable, viewController in
            viewController.stopwatch = resolvable.resolve( Stopwatch.self )!
            viewController.image = resolvable.resolve( ImageService.self )!
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是Stopwatch.swift,它只是在触发 onComplete 处理程序之前暂停一段时间:

import Foundation

class Stopwatch: StopwatchProtocol
{
    var key: String { return "Stopwatch_\( _key ).Complete" }

    private var
    _signals: SignalsProtocol
    , _key: UInt16
    , _timer: NSTimer?
    , _data: AnyObject?

    func startWith ( 
        Delay delay: Double
        , ForListener closure: ( String, Any? ) -> Void 
    ){
        _data = nil
        _startWith( Delay: delay, ForListener: closure )
    }

    func stop ()
    {
        guard let timer = _timer else { return }
        timer.invalidate()
        _timer = nil
        _data = nil
    }

    private func _startWith ( 
        Delay delay: Double
        , ForListener closure: ( String, Any? ) -> Void 
    ){
        stop()

        _timer = NSTimer.scheduledTimerWithTimeInterval(
            NSTimeInterval( delay )
            , target: self
            , selector: #selector( _onTimerComplete )
            , userInfo: nil
            , repeats: false
        )
    }

    @objc private func _onTimerComplete ()
    {
        stop()
        print( "stopwatch with key `\( key )` complete." )
    }

    required init ( signals: SignalsProtocol )
    {
        _signals = signals
        _key = getPrimaryKey()
        print( "primary key: \( _key )" )
    }
}
Run Code Online (Sandbox Code Playgroud)

ImageService.swift目前仅通过函数接受信号和秒表属性init

protocol ImageProtocol {}

class ImageService: ImageProtocol
{
    private let
  _signals: SignalsProtocol
    , _stopwatch: StopwatchProtocol

    required init ( 
    signals: SignalsProtocol
    , stopwatch: StopwatchProtocol 
  ){
        _signals = signals
        _stopwatch = stopwatch

        lo( "ImageService key: \( _stopwatch.key )" )
    }
}
Run Code Online (Sandbox Code Playgroud)

SignalsService.swift目前是一个空的 Model 类:

protocol SignalsProtocol {}

class SignalsService: SignalsProtocol {}
Run Code Online (Sandbox Code Playgroud)

WhileStartUpViewController.swift是一个基本的UIViewController,目前只接受其注入的属性:

import UIKit

class StartUpViewController: UIViewController
{
    var image: ImageService? { 
        willSet { 
            guard _image == nil else { return }
            _image = newValue
        }
    }

    var signals: SignalsService? { 
        willSet { 
            guard _signals == nil else { return }
            _signals = newValue 
        }
    }

    var stopwatch: StopwatchProtocol? { 
        willSet { 
            guard _stopwatch == nil else { return }
            _stopwatch = newValue
            print( "StartUpViewController key: \( _stopwatch.key )" ) 
        } 
    }

    internal var
    _image: ImageService!
    , _signals: SignalsService!
    , _stopwatch: Stopwatch!    
}
Run Code Online (Sandbox Code Playgroud)

最后getPrivateKey()是一个全局静态,返回唯一的整数:

private var _primaryKey = UInt16( 0 )

func getPrimaryKey () -> UInt16
{
    _primaryKey += 1
    return _primaryKey
}
Run Code Online (Sandbox Code Playgroud)

现在,据我了解,我注册的方式Stopwatch.swift意味着MySwinjectStoryboard.swift每次注入实例时,它将是一个新的离散实例。但是, 和ImageService.swiftStartUpViewController.swift被注入同一个实例:

StartUpViewController key: Stopwatch_2.Complete
ImageService key: Stopwatch_2.Complete
Run Code Online (Sandbox Code Playgroud)

ImageService的关键应该是:

ImageService key: Stopwatch_3.Complete
Run Code Online (Sandbox Code Playgroud)

请问有人知道为什么会发生这种情况吗?谢谢。

Jak*_*ano 5

服务的默认范围是.Graph. 来自文档

对于 ObjectScope.Graph,如果直接调用容器的resolve方法,总是会创建一个实例,就像在ObjectScope.None中一样,但是在工厂闭包中解析的实例在解析根实例以构造对象图期间会共享。

如果您希望即使在对象图解析期间也为每个引用创建唯一的实例,则应该使用对象范围.None,即

defaultContainer.register(Stopwatch.self) { resolver in 
    Stopwatch(signals: resolver.resolve(SignalsService.self)!)
}.inObjectScope(.None)
Run Code Online (Sandbox Code Playgroud)