You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

343 lines
13 KiB

2 years ago
  1. //
  2. // Copyright © 2019 Swinject Contributors. All rights reserved.
  3. //
  4. import Foundation
  5. /// The `Container` class represents a dependency injection container, which stores registrations of services
  6. /// and retrieves registered services with dependencies injected.
  7. ///
  8. /// **Example to register:**
  9. ///
  10. /// let container = Container()
  11. /// container.register(A.self) { _ in B() }
  12. /// container.register(X.self) { r in Y(a: r.resolve(A.self)!) }
  13. ///
  14. /// **Example to retrieve:**
  15. ///
  16. /// let x = container.resolve(X.self)!
  17. ///
  18. /// where `A` and `X` are protocols, `B` is a type conforming `A`, and `Y` is a type conforming `X`
  19. /// and depending on `A`.
  20. public final class Container {
  21. internal var services = [ServiceKey: ServiceEntryProtocol]()
  22. private let parent: Container? // Used by HierarchyObjectScope
  23. private var resolutionDepth = 0
  24. private let debugHelper: DebugHelper
  25. private let defaultObjectScope: ObjectScope
  26. internal var currentObjectGraph: GraphIdentifier?
  27. internal let lock: SpinLock // Used by SynchronizedResolver.
  28. internal var behaviors = [Behavior]()
  29. internal init(
  30. parent: Container? = nil,
  31. debugHelper: DebugHelper,
  32. defaultObjectScope: ObjectScope = .graph
  33. ) {
  34. self.parent = parent
  35. self.debugHelper = debugHelper
  36. lock = parent.map { $0.lock } ?? SpinLock()
  37. self.defaultObjectScope = defaultObjectScope
  38. }
  39. /// Instantiates a `Container`
  40. ///
  41. /// - Parameters
  42. /// - parent: The optional parent `Container`.
  43. /// - defaultObjectScope: Default object scope (graph if no scope is injected)
  44. /// - behaviors: List of behaviors to be added to the container
  45. /// - registeringClosure: The closure registering services to the new container instance.
  46. ///
  47. /// - Remark: Compile time may be long if you pass a long closure to this initializer.
  48. /// Use `init()` or `init(parent:)` instead.
  49. public convenience init(
  50. parent: Container? = nil,
  51. defaultObjectScope: ObjectScope = .graph,
  52. behaviors: [Behavior] = [],
  53. registeringClosure: (Container) -> Void = { _ in }
  54. ) {
  55. self.init(parent: parent, debugHelper: LoggingDebugHelper(), defaultObjectScope: defaultObjectScope)
  56. behaviors.forEach(addBehavior)
  57. registeringClosure(self)
  58. }
  59. /// Removes all registrations in the container.
  60. public func removeAll() {
  61. services.removeAll()
  62. }
  63. /// Discards instances for services registered in the given `ObjectsScopeProtocol`.
  64. ///
  65. /// **Example usage:**
  66. /// container.resetObjectScope(ObjectScope.container)
  67. ///
  68. /// - Parameters:
  69. /// - objectScope: All instances registered in given `ObjectsScopeProtocol` will be discarded.
  70. public func resetObjectScope(_ objectScope: ObjectScopeProtocol) {
  71. services.values
  72. .filter { $0.objectScope === objectScope }
  73. .forEach { $0.storage.instance = nil }
  74. parent?.resetObjectScope(objectScope)
  75. }
  76. /// Discards instances for services registered in the given `ObjectsScope`. It performs the same operation
  77. /// as `resetObjectScope(_:ObjectScopeProtocol)`, but provides more convenient usage syntax.
  78. ///
  79. /// **Example usage:**
  80. /// container.resetObjectScope(.container)
  81. ///
  82. /// - Parameters:
  83. /// - objectScope: All instances registered in given `ObjectsScope` will be discarded.
  84. public func resetObjectScope(_ objectScope: ObjectScope) {
  85. resetObjectScope(objectScope as ObjectScopeProtocol)
  86. }
  87. /// Adds a registration for the specified service with the factory closure to specify how the service is
  88. /// resolved with dependencies.
  89. ///
  90. /// - Parameters:
  91. /// - serviceType: The service type to register.
  92. /// - name: A registration name, which is used to differentiate from other registrations
  93. /// that have the same service and factory types.
  94. /// - factory: The closure to specify how the service type is resolved with the dependencies of the type.
  95. /// It is invoked when the `Container` needs to instantiate the instance.
  96. /// It takes a `Resolver` to inject dependencies to the instance,
  97. /// and returns the instance of the component type for the service.
  98. ///
  99. /// - Returns: A registered `ServiceEntry` to configure more settings with method chaining.
  100. @discardableResult
  101. public func register<Service>(
  102. _ serviceType: Service.Type,
  103. name: String? = nil,
  104. factory: @escaping (Resolver) -> Service
  105. ) -> ServiceEntry<Service> {
  106. return _register(serviceType, factory: factory, name: name)
  107. }
  108. /// This method is designed for the use to extend Swinject functionality.
  109. /// Do NOT use this method unless you intend to write an extension or plugin to Swinject framework.
  110. ///
  111. /// - Parameters:
  112. /// - serviceType: The service type to register.
  113. /// - factory: The closure to specify how the service type is resolved with the dependencies of the type.
  114. /// It is invoked when the `Container` needs to instantiate the instance.
  115. /// It takes a `Resolver` to inject dependencies to the instance,
  116. /// and returns the instance of the component type for the service.
  117. /// - name: A registration name.
  118. /// - option: A service key option for an extension/plugin.
  119. ///
  120. /// - Returns: A registered `ServiceEntry` to configure more settings with method chaining.
  121. @discardableResult
  122. // swiftlint:disable:next identifier_name
  123. public func _register<Service, Arguments>(
  124. _ serviceType: Service.Type,
  125. factory: @escaping (Arguments) -> Any,
  126. name: String? = nil,
  127. option: ServiceKeyOption? = nil
  128. ) -> ServiceEntry<Service> {
  129. let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option)
  130. let entry = ServiceEntry(
  131. serviceType: serviceType,
  132. argumentsType: Arguments.self,
  133. factory: factory,
  134. objectScope: defaultObjectScope
  135. )
  136. entry.container = self
  137. services[key] = entry
  138. behaviors.forEach { $0.container(self, didRegisterType: serviceType, toService: entry, withName: name) }
  139. return entry
  140. }
  141. /// Returns a synchronized view of the container for thread safety.
  142. /// The returned container is `Resolver` type. Call this method after you finish all service registrations
  143. /// to the original container.
  144. ///
  145. /// - Returns: A synchronized container as `Resolver`.
  146. public func synchronize() -> Resolver {
  147. return SynchronizedResolver(container: self)
  148. }
  149. /// Adds behavior to the container. `Behavior.container(_:didRegisterService:withName:)` will be invoked for
  150. /// each service registered to the `container` **after** the behavior has been added.
  151. ///
  152. /// - Parameters:
  153. /// - behavior: Behavior to be added to the container
  154. public func addBehavior(_ behavior: Behavior) {
  155. behaviors.append(behavior)
  156. }
  157. internal func restoreObjectGraph(_ identifier: GraphIdentifier) {
  158. currentObjectGraph = identifier
  159. }
  160. }
  161. // MARK: - _Resolver
  162. extension Container: _Resolver {
  163. // swiftlint:disable:next identifier_name
  164. public func _resolve<Service, Arguments>(
  165. name: String?,
  166. option: ServiceKeyOption? = nil,
  167. invoker: @escaping ((Arguments) -> Any) -> Any
  168. ) -> Service? {
  169. var resolvedInstance: Service?
  170. let key = ServiceKey(serviceType: Service.self, argumentsType: Arguments.self, name: name, option: option)
  171. if let entry = getEntry(for: key) {
  172. resolvedInstance = resolve(entry: entry, invoker: invoker)
  173. }
  174. if resolvedInstance == nil {
  175. resolvedInstance = resolveAsWrapper(name: name, option: option, invoker: invoker)
  176. }
  177. if resolvedInstance == nil {
  178. debugHelper.resolutionFailed(
  179. serviceType: Service.self,
  180. key: key,
  181. availableRegistrations: getRegistrations()
  182. )
  183. }
  184. return resolvedInstance
  185. }
  186. fileprivate func resolveAsWrapper<Wrapper, Arguments>(
  187. name: String?,
  188. option: ServiceKeyOption?,
  189. invoker: @escaping ((Arguments) -> Any) -> Any
  190. ) -> Wrapper? {
  191. guard let wrapper = Wrapper.self as? InstanceWrapper.Type else { return nil }
  192. let key = ServiceKey(
  193. serviceType: wrapper.wrappedType, argumentsType: Arguments.self, name: name, option: option
  194. )
  195. if let entry = getEntry(for: key) {
  196. let factory = { [weak self] in self?.resolve(entry: entry, invoker: invoker) as Any? }
  197. return wrapper.init(inContainer: self, withInstanceFactory: factory) as? Wrapper
  198. } else {
  199. return wrapper.init(inContainer: self, withInstanceFactory: nil) as? Wrapper
  200. }
  201. }
  202. fileprivate func getRegistrations() -> [ServiceKey: ServiceEntryProtocol] {
  203. var registrations = parent?.getRegistrations() ?? [:]
  204. services.forEach { key, value in registrations[key] = value }
  205. return registrations
  206. }
  207. fileprivate var maxResolutionDepth: Int { return 200 }
  208. fileprivate func incrementResolutionDepth() {
  209. parent?.incrementResolutionDepth()
  210. if resolutionDepth == 0, currentObjectGraph == nil {
  211. currentObjectGraph = GraphIdentifier()
  212. }
  213. guard resolutionDepth < maxResolutionDepth else {
  214. fatalError("Infinite recursive call for circular dependency has been detected. " +
  215. "To avoid the infinite call, 'initCompleted' handler should be used to inject circular dependency.")
  216. }
  217. resolutionDepth += 1
  218. }
  219. fileprivate func decrementResolutionDepth() {
  220. parent?.decrementResolutionDepth()
  221. assert(resolutionDepth > 0, "The depth cannot be negative.")
  222. resolutionDepth -= 1
  223. if resolutionDepth == 0 { graphResolutionCompleted() }
  224. }
  225. fileprivate func graphResolutionCompleted() {
  226. services.values.forEach { $0.storage.graphResolutionCompleted() }
  227. currentObjectGraph = nil
  228. }
  229. }
  230. // MARK: - Resolver
  231. extension Container: Resolver {
  232. /// Retrieves the instance with the specified service type.
  233. ///
  234. /// - Parameter serviceType: The service type to resolve.
  235. ///
  236. /// - Returns: The resolved service type instance, or nil if no registration for the service type
  237. /// is found in the `Container`.
  238. public func resolve<Service>(_ serviceType: Service.Type) -> Service? {
  239. return resolve(serviceType, name: nil)
  240. }
  241. /// Retrieves the instance with the specified service type and registration name.
  242. ///
  243. /// - Parameters:
  244. /// - serviceType: The service type to resolve.
  245. /// - name: The registration name.
  246. ///
  247. /// - Returns: The resolved service type instance, or nil if no registration for the service type and name
  248. /// is found in the `Container`.
  249. public func resolve<Service>(_: Service.Type, name: String?) -> Service? {
  250. return _resolve(name: name) { (factory: (Resolver) -> Any) in factory(self) }
  251. }
  252. fileprivate func getEntry(for key: ServiceKey) -> ServiceEntryProtocol? {
  253. if let entry = services[key] {
  254. return entry
  255. } else {
  256. return parent?.getEntry(for: key)
  257. }
  258. }
  259. fileprivate func resolve<Service, Factory>(
  260. entry: ServiceEntryProtocol,
  261. invoker: (Factory) -> Any
  262. ) -> Service? {
  263. incrementResolutionDepth()
  264. defer { decrementResolutionDepth() }
  265. guard let currentObjectGraph = currentObjectGraph else {
  266. fatalError("If accessing container from multiple threads, make sure to use a synchronized resolver.")
  267. }
  268. if let persistedInstance = persistedInstance(Service.self, from: entry, in: currentObjectGraph) {
  269. return persistedInstance
  270. }
  271. let resolvedInstance = invoker(entry.factory as! Factory)
  272. if let persistedInstance = persistedInstance(Service.self, from: entry, in: currentObjectGraph) {
  273. // An instance for the key might be added by the factory invocation.
  274. return persistedInstance
  275. }
  276. entry.storage.setInstance(resolvedInstance as Any, inGraph: currentObjectGraph)
  277. if let completed = entry.initCompleted as? (Resolver, Any) -> Void,
  278. let resolvedInstance = resolvedInstance as? Service {
  279. completed(self, resolvedInstance)
  280. }
  281. return resolvedInstance as? Service
  282. }
  283. private func persistedInstance<Service>(
  284. _: Service.Type, from entry: ServiceEntryProtocol, in graph: GraphIdentifier
  285. ) -> Service? {
  286. if let instance = entry.storage.instance(inGraph: graph), let service = instance as? Service {
  287. return service
  288. } else {
  289. return nil
  290. }
  291. }
  292. }
  293. // MARK: CustomStringConvertible
  294. extension Container: CustomStringConvertible {
  295. public var description: String {
  296. return "["
  297. + services.map { "\n { \($1.describeWithKey($0)) }" }.sorted().joined(separator: ",")
  298. + "\n]"
  299. }
  300. }