你好!我叫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
, . .
, - , , .
. :
- homebrew:
brew install needle
- :
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 , , , , .