-
Notifications
You must be signed in to change notification settings - Fork 72
Circular dependencies
Circular dependency is the situation when you have two components referencing each other. Generally you should avoid that. But Dip can help you to resolve circular dependencies. For that you need to register your components with Shared
scope, so that they will be reused in a single object graph, and use resolveDependencies
method of DefinitionOf
returned by register
method:
container.register(.Shared) {
try ClientImp(server: container.resolve() as Server) as Client
}
container.register(.Shared) { ServerImp() as Server }
.resolvingProperties { container, server in
server.client = try container.resolve() as Client
}
In this example we have a Server
that has a reference to a Client
and a Client
that has a reference to a Server
. Client
uses constructor injection to inject an instance of a Server
. Then Server
should use property injection to inject a Client
. Otherwise we will have infinite recursion.
When you ask container to resolve a Client
you will get an instance of ClientImp
with reference to ServerImp
which will have a reference to the same ClientImp
instance.
let client = try! container.resolve() as Client
client === client.server.client
Note: don't forget about retain cycles - if you have circular dependencies one of them should be stored in a
weak
reference. IfClient
holdsweak
reference toServer
then you will need to resolveServer
first, so that it does not get released whenresolve
returns:
class ClientImp: Client {
weak var server: Server
...
}
class ServerImp: Server {
var client: Client
...
}
let client = try! container.resolve() as Client
client.server // will be nil because no one reference `Server` instance strongly before `resolve` returns
let server = try! container.resolve() as Server
server.client.server //will be not nil, because `server` is a strong reference to `Server` instance.
It may happen that one of the instances involved in circular dependency will be instantiated twice (one that uses constructor injection). Then container will ignore the second instance and will use only the first one. If you want to avoid such situation you have two options:
- using property injection for both instances;
- refactoring your code to remove circular dependency. Instead of two instances referencing each other define the third instance on which they both will depend;