介绍Needle,一个Swift依赖注入系统

你好!我叫Anton,我是Joom的iOS开发人员。在本文中,您将学习我们如何与Needle DI框架一起使用,以及它是否真的可以与类似的解决方案相提并论,是否可以在生产代码中使用。仅此而已-当然,还有性能评估。





背景



, iOS Objective-C, DI-, Typhoon. , Typhoon overhead runtime, .



Joom , , . , iOS- , .



Objective-C Swift, . ?



Swift, Objective-C DI. , : .

, runtime. property, :



  • , ;
  • ;
  • .

    lazy property . , , runtime.


, compile time. - header implementation, - , .



, .



, DI- . 5-6 .



, :



  • forward declaration .h- ;
  • .h- ;
  • #import header .m- ;
  • .m- ;
  • , .


, ? .



, C. copy/paste , .



. , , . .



. , . , SOLID, , , , . Objective-C.



, , 2018.

, « » , .

Swift Objective-C.



DI .



framework, , Objective-C. boilerplate .



DI framework- Swift. C Swinject Dip. .



:



  • runtime. , , , .
  • runtime, .
  • , force unwrap ! (Swinject) try! (Dip) , .


, . , DI framework Needle.





Needle — open-source Uber, Swift 2018 ( — 7 2018).



compile time safety .



.



Needle : NeedleFoundation framework.





DI . SourceKit.



. DependencyProvider, . .



, - , , .



. :



  1. homebrew:

    brew install needle
  2. :

    git clone https://github.com/uber/needle.git & cd Generator/bin/needle


Run Script , , . :



export SOURCEKIT_LOGGING=0 && needle generate ../NeedleGenerated.swift


../NeedleGenerated.swift — , .



NeedleFoundation



NeedleFoundation — , .



. CocoaPods:



pod 'NeedleFoundation'


root-, BootstrapComponent.



Component.

DI- , Dependency generic type- .



:



protocol SomeUIDependency: Dependency {
    var applicationURLHandler: ApplicationURLHandler { get }
    var router: Router { get }
}

final class SomeUIComponent: Component<SomeDependency> {
    ...
}


, <EmptyDependency>.



DI- lazy- path name:



// Component.swift
public lazy var path: [String] = {
        let name = self.name
        return parent.path + ["\(name)"]
}()

private lazy var name: String = {
    let fullyQualifiedSelfName = String(describing: self)
    let parts = fullyQualifiedSelfName.components(separatedBy: ".")
    return parts.last ?? fullyQualifiedSelfName
}()


, DI- .



, :



RootComponent->UIComponent->SupportUIComponent,



SupportUIComponent path [RootComponent, UIComponent, SupportUIComponent].



DI- DependencyProvider , singleton- __DependencyProviderRegistry:



// Component.swift
public init(parent: Scope) {
     self.parent = parent
     dependency = createDependencyProvider()
}

// ...

private func createDependencyProvider() -> DependencyType {
    let provider = __DependencyProviderRegistry.instance.dependencyProvider(for: self)
    if let dependency = provider as? DependencyType {
        return dependency
    } else {
        // This case should never occur with properly generated Needle code.
        // Needle's official generator should guarantee the correctness.
        fatalError("Dependency provider factory for \(self) returned incorrect type. Should be of type \(String(describing: DependencyType.self)). Actual type is \(String(describing: dependency))")
    }
}


, DependencyProvider __DependencyProviderRegistry path. , . hash , :



// DependencyProviderRegistry.swift
func dependencyProvider(`for` component: Scope) -> AnyObject {
    providerFactoryLock.lock()
    defer {
        providerFactoryLock.unlock()
    }

    let pathString = component.path.joined(separator: "->")
    if let factory = providerFactories[pathString.hashValue] {
        return factory(component)
    } else {
        // This case should never occur with properly generated Needle code.
        // This is useful for Needle generator development only.
          fatalError("Missing dependency provider factory for \(component.path)")
    }
}


DependencyProvider dependency, .



:



protocol SomeUIDependency: Dependency {
    var applicationURLHandler: ApplicationURLHandler { get }
    var router: Router { get }
}

final class SomeUIComponent: Component<SomeDependency> {
    var someObject: SomeObjectClass {
        shared {
            SomeObjectClass(router: dependecy.router)
        }
    }
}


DependecyProvider.



DependencyProvider



, DI- DependencyProvider. . Needle DI- BootstrapComponent Component.



DI- .



. , .. .



.



, , . compile-time safety.



, , Needle DependecyProvider DI-. :



// NeedleGenerated.swift

/// ^->RootComponent->UIComponent->SupportUIComponent->SomeUIComponent
private class SomeUIDependencyfb16d126f544a2fb6a43Provider: SomeUIDependency {
    var applicationURLHandler: ApplicationURLHandler {
        return supportUIComponent.coreComponents.applicationURLHandler
    }
    // ...
}


- , , DependecyProvider . compile-time safety Needle.



.



DependencyProvider



DependecyProvider , Needle .



closure-, . .



registerProviderFactories(), - DI-.



// NeedleGenerated.swift
public func registerProviderFactories() {
    __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: "^->RootComponent") { component in
        return EmptyDependencyProvider(component: component)
    }
    __DependencyProviderRegistry.instance.registerDependencyProviderFactory(for: "^->RootComponent->UIComponent") { component in
        return EmptyDependencyProvider(component: component)
    }
        // ...
}   


singleton- __DependencyProviderRegistry. [Int: (Scope) -> AnyObject], hashValue , , — closure-. thread-safe NSRecursiveLock.



// DependencyProviderRegistry.swift
public func registerDependencyProviderFactory(`for` componentPath: String, _ dependencyProviderFactory: @escaping (Scope) -> AnyObject) {
    providerFactoryLock.lock()
    defer {
        providerFactoryLock.unlock()
    }

    providerFactories[componentPath.hashValue] = dependencyProviderFactory
}




430 . 83 Swift.



iPhone 11 c iOS 13.3.1 Needle 0.14.



develop , root- needle-, Needle. .







Needle Needle
1 294.5s 295.1s
2 280.8s 286.4s
3 268.2s 294.1s 
4 282.9s 279.5s
5 291.5s 293.4s


Needle: 283.58s



Needle: 289.7s



, , Needle, +6 .





Needle Needle
1 37.8s 36.1s
2 27.9s 37.0s
3 37.3s 33.0s 
4 38.2s 35.5s
5 37.8s 35.8s


Needle: 35.8s



Needle: 35.48s



.



registerProviderFactories()



(): 0.000103



:



0.0001500844955444336
0.0000939369201660156
0.0000900030136108398
0.0000920295715332031
0.0001270771026611328
0.0000950098037719726
0.0000910758972167968
0.0000970363616943359
0.0000969171524047851
0.0000959634780883789


, Needle .





Needle Needle C Needle + FakeComponents
1 0.000069 0.001111 0.002981
2 0.000103 0.001153 0.002657
3 0.000080 0.001132 0.002418
4 0.000096 0.001142 0.002812
5 0.000078 0.001177 0.001960


Needle (): 0.000085



C Needle (): 0.001143 (+0.001058)



C Needle + FakeComponents (): 0.002566



: SomeUIComponent :^->RootComponent->UIComponent->SupportUIComponent->SupportUIFake0Component->SupportUIFake1Component->SupportUIFake2Component->SupportUIFake3Component->SomeUIComponent



. , . , .



BabyloneUIComponent c Needle



Needle Needle C Needle + FakeComponents
1 0.000031 0.000069 0.000088
2 0.000037 0.000049 0.000100
3 0.000053 0.000054 0.000082
4 0.000057 0.000064 0.000092
5 0.000041 0.000053 0.000088


Needle (): 0.000044



Needle (): 0.000058



Needle + FakeComponents ():0.000091



. , .





, Needle , DI-.



compile time safety .



. , Objective-C, .



. .



Needle , - . , .



, Needle , , , , .




All Articles