Skip to content

Circular dependencies

Ilya Puchka edited this page Sep 24, 2016 · 5 revisions

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. If Client holds weak reference to Server then you will need to resolve Server first, so that it does not get released when resolve 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;