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

Circular dependencies #11

Merged
merged 12 commits into from
Nov 22, 2015
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# CHANGELOG

## Develop

#### New Features

* Added support for circular dependencies by adding `ObjectGraph` scope to reuse resolved instances.
[#11](https://github.com/AliSoftware/Dip/pull/11), [@ilyapuchka](https://github.com/ilyapuchka)
* Added methods to register/remove individual definitions.
[#11](https://github.com/AliSoftware/Dip/pull/11), [@ilyapuchka](https://github.com/ilyapuchka)

#### Breaking Changes

* Removed container thread-safety to enable recursion calls to `resolve`.
**Access to container from multiple threads should be handled by clients** from now on.
* Deprecated `register(tag:instance:)` method in favor of `register(.Singleton, …)`.

## 3.0.0

* Added support for factories with up to six runtime arguments.
Expand Down
6 changes: 6 additions & 0 deletions Dip/Dip.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
094526B41BEA51540034E72A /* RuntimeArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094526B31BEA51540034E72A /* RuntimeArguments.swift */; };
094526B61BEA520B0034E72A /* Definition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094526B51BEA520B0034E72A /* Definition.swift */; };
094526B81BEA536A0034E72A /* RuntimeArgumentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094526B71BEA536A0034E72A /* RuntimeArgumentsTests.swift */; };
0989323F1BEBC8CD00ACDA2B /* ComponentScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989323E1BEBC8CD00ACDA2B /* ComponentScopeTests.swift */; };
09969C551BEB7C0A00F93C70 /* Dip.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 09969C541BEB7C0A00F93C70 /* Dip.podspec */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -37,6 +39,8 @@
094526B31BEA51540034E72A /* RuntimeArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = RuntimeArguments.swift; sourceTree = "<group>"; tabWidth = 2; };
094526B51BEA520B0034E72A /* Definition.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = Definition.swift; sourceTree = "<group>"; tabWidth = 2; };
094526B71BEA536A0034E72A /* RuntimeArgumentsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuntimeArgumentsTests.swift; sourceTree = "<group>"; };
0989323E1BEBC8CD00ACDA2B /* ComponentScopeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComponentScopeTests.swift; sourceTree = "<group>"; };
09969C541BEB7C0A00F93C70 /* Dip.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Dip.podspec; path = ../Dip.podspec; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -95,6 +99,7 @@
children = (
094526A01BEA1CFF0034E72A /* DipTests.swift */,
094526B71BEA536A0034E72A /* RuntimeArgumentsTests.swift */,
0989323E1BEBC8CD00ACDA2B /* ComponentScopeTests.swift */,
094526A21BEA1CFF0034E72A /* Info.plist */,
);
path = DipTests;
Expand Down Expand Up @@ -219,6 +224,7 @@
buildActionMask = 2147483647;
files = (
094526A11BEA1CFF0034E72A /* DipTests.swift in Sources */,
0989323F1BEBC8CD00ACDA2B /* ComponentScopeTests.swift in Sources */,
094526B81BEA536A0034E72A /* RuntimeArgumentsTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
57 changes: 49 additions & 8 deletions Dip/Dip/Definition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,70 @@ func ==(lhs: DefinitionKey, rhs: DefinitionKey) -> Bool {
public enum ComponentScope {
/// Indicates that a new instance of the component will be created each time it's resolved.
case Prototype
/// Indicates that instances will be reused during resolve but will be discurded when topmost `resolve` method returns.
case ObjectGraph
/// Indicates that resolved component should be retained by container and always reused.
case Singleton
}

///Definition of type T describes how instances of this type should be created when they are resolved by container.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this has two generic parameters, maybe better describe them in the comments, like "T is the type of the instance to resolve to, F is the type of the factory to generate that instance, namely the runtime parameters that needs to be given when resolving that instance" or sthg similar.

public final class DefinitionOf<T>: Definition {
let factory: Any
let scope: ComponentScope
public struct DefinitionOf<T, F>: Definition {

init(factory: Any, scope: ComponentScope = .Prototype) {
/**
Sets the block that will be used to resolve dependencies of the component.
This block will be called before `resolve` returns.

- parameter block: block to use to resolve dependencies

- note:
If you have circular dependencies at least one of them should use this block
to resolve it's dependencies. Otherwise code enter infinite loop.

**Example**

```swift
container.register { [unowned container] ClientImp(service: container.resolve() as Service) as Client }

var definition = container.register { ServiceImp() as Service }
definition.resolveDependencies { container, service in
service.delegate = container.resolve() as Client
}
```

*/
public func resolveDependencies(container: DependencyContainer, block: (DependencyContainer, T) -> ()) -> DefinitionOf<T, F> {
guard resolveDependenciesBlock == nil else {
fatalError("You can not change resolveDependencies block after it was set.")
}
var newDefinition = self
newDefinition.resolveDependenciesBlock = block
container.register(newDefinition)
return newDefinition
}

let factory: F
var scope: ComponentScope
var resolveDependenciesBlock: ((DependencyContainer, T) -> ())?
let tag: DependencyContainer.Tag?

init(factory: F, scope: ComponentScope, tag: DependencyContainer.Tag?) {
self.factory = factory
self.scope = scope
self.tag = tag
}

///Will be stored only if scope is `Singleton`
var resolvedInstance: T? {
get {
guard scope == .Singleton else { return nil }
return _resolvedInstance
}
set {
guard scope == .Singleton else { return }
_resolvedInstance = newValue
}
}

mutating func resolvedInstance(container: DependencyContainer, tag: DependencyContainer.Tag? = nil, instance: T) {
guard scope == .Singleton else { return }
_resolvedInstance = instance
container.register(self)
}

private var _resolvedInstance: T?
Expand Down
24 changes: 20 additions & 4 deletions Dip/Dip/Dip.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
//
// Dip.h
// Dip
// Dip
//
// Created by Ilya Puchka on 04.11.15.
// Copyright © 2015 AliSoftware. All rights reserved.
// Copyright (c) 2015 Olivier Halligon <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#import <Foundation/Foundation.h>
Expand Down
Loading