Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Singleton not being released after unregister in collaboration container #203

Open
xrayman opened this issue Sep 7, 2018 · 6 comments
Open

Comments

@xrayman
Copy link

xrayman commented Sep 7, 2018

Hello!
Here's the situation. I have some singletons which are make sense only if user is logged in. So after user is logged out I want to release all those singletons and recreate new instances after the next login. To do that I've separated all those singleton into another container and collaborate it with the main container. Then I register singletons on login and clear second container on logout.
The problem is after I clear the second container singletons are not getting released because main container retains them (but I can't resolve them from main container). They are released only after next login when I register and resolve them again.

Is this an intended behavior?

Code example:

import UIKit
import RxSwift
import RxCocoa
import Dip

public protocol TestClassType: class {
    var count: Int { get }
}
public class TestClass: TestClassType {
    private static var _count: Int = 0
    public let count: Int

    init() {
        count = TestClass._count
        TestClass._count += 1
        print("[😇init] TestClass: \(count)")
    }

    deinit {
        print("[😈deinit] TestClass: \(count)")
    }
}

private var _container: DependencyContainer?

extension DependencyContainer {
    func registerSessionRelatedStuff() {
        print("registerSessionRelatedStuff")
        let container = DependencyContainer()
        container.register(.singleton){ TestClass() as TestClassType }
        collaborate(with: container)
        _container = container
    }

    func unregisterSessionRelatedStuff() {
        print("unregisterSessionRelatedStuff")

        weak var item = try? resolve() as TestClassType
        print("Here we are still able to resolve type: \(item)")

        print("====== reset ======")
        _container?.reset()
        _container = nil

        let item2 = try? resolve() as TestClassType
        print("Here we are not able to resolve type anymore: \(item2)")
        print("But the instance is still alive: \(item)")
    }
}

var loggedIn = BehaviorSubject<Bool>(value: false)
var loggedInDriver = loggedIn.asDriver(onErrorDriveWith: .empty()).distinctUntilChanged()

class ViewController: UIViewController {
    @IBOutlet weak var button: UIButton!

    private var container = DependencyContainer()

    private var isWorking = BehaviorRelay<Bool>(value: false)
    private lazy var isWorkingDriver: Driver<Bool> = { isWorking.asDriver(onErrorDriveWith: .empty()).distinctUntilChanged() }()

    private var disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        loggedInDriver.filter{ $0 }.map{ _ in }.drive(onNext: container.registerSessionRelatedStuff).disposed(by: disposeBag)
        loggedInDriver.filter{ !$0 }.map{ _ in }.drive(onNext: container.unregisterSessionRelatedStuff).disposed(by: disposeBag)
        loggedInDriver.map{ _ in false }.drive(isWorking).disposed(by: disposeBag)
        loggedInDriver.map{ $0 ? "Logout" : "Login" }.drive(button.rx.title()).disposed(by: disposeBag)
        isWorkingDriver.map(!).drive(button.rx.isEnabled).disposed(by: disposeBag)

        button.rx.tap.asSignal().withLatestFrom(loggedInDriver).map(!).do(onNext: { [weak self] in
            self?.isWorking.accept(true)
            print("Loggin \($0 ? "IN" : "OUT")")
        }).delay(1.0).emit(to: loggedIn).disposed(by: disposeBag)

        loggedInDriver.filter{ $0 }.delay(0.5).drive(onNext: { [weak self] _ in
            guard let _self = self else { return }
            print("Previous instance will only die here")
            _ = try? _self.container.resolve() as TestClassType
        }).disposed(by: disposeBag)
    }
}
@ilyapuchka
Copy link
Collaborator

Maybe try weakSingletone scope?

@xrayman
Copy link
Author

xrayman commented Sep 7, 2018

weakSingleton could help, but I will need to retain somewhere instance myself. Well yes, I can find workaround for this. But I want to mention this issue to Dip developers. I think it should be fixed somehow.

@ilyapuchka
Copy link
Collaborator

@xrayman can you add a test that demonstrates this?

@xrayman
Copy link
Author

xrayman commented Sep 24, 2018

@ilyapuchka What do you mean by "test"? Test project?

@ilyapuchka
Copy link
Collaborator

no, unit test is enough

@xrayman
Copy link
Author

xrayman commented Sep 24, 2018

@ilyapuchka insert tests into DipTests.swift

  // will pass
  func testSingletonsInCollaborationUsingCollaborator() {
    let collaborator = DependencyContainer()
    collaborator.register(.singleton) { ResolvableService() as Service }
    container.collaborate(with: collaborator)
    
    weak var service = try? collaborator.resolve() as Service
    XCTAssertNotNil(service)
    
    collaborator.reset()
    
    let noResolveContainer = try? container.resolve() as Service
    let noResolveCollaborator = try? collaborator.resolve() as Service
    XCTAssertNil(noResolveContainer)
    XCTAssertNil(noResolveCollaborator)
    
    XCTAssertNil(service)
  }

  // will fail
  func testSingletonsInCollaborationUsingContainer() {
    let collaborator = DependencyContainer()
    collaborator.register(.singleton) { ResolvableService() as Service }
    container.collaborate(with: collaborator)

    weak var service = try? container.resolve() as Service
    XCTAssertNotNil(service)

    collaborator.reset()

    let noResolveContainer = try? container.resolve() as Service
    let noResolveCollaborator = try? collaborator.resolve() as Service
    XCTAssertNil(noResolveContainer)
    XCTAssertNil(noResolveCollaborator)

    XCTAssertNil(service)
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants