Hello! My name is Anton, I am an iOS developer at Joom. In this article, you will learn how we work with the Needle DI framework, and whether it really compares favorably with similar solutions and is ready for use in production code. That's all - with performance measurements, of course.
Background
, 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 , , , , .