SwinjectAutoregistration
SwinjectAutoregistration 是 Swinject 的一个扩展,它允许您自动注册服务,从而大大减少样板代码的数量。
需求
- iOS 9.0+ / Mac OS X 10.10+ / tvOS 9.0+
- Xcode 8+
安装
Swinject 通过 Carthage、CocoaPods 或 Swift Package Manager 可用。
Carthage
要使用Carthage安装Swinject,请将以下行添加到您的Cartfile
中。
github "Swinject/Swinject" "2.8.3"
github "Swinject/SwinjectAutoregistration" "2.8.3"
然后运行carthage update --use-xcframeworks --no-use-binaries
命令或仅运行carthage update --use-xcframeworks
。有关Carthage的安装和使用详情,请访问其项目页面。
CocoaPods
要使用CocoaPods安装Swinject,请将以下行添加到您的Podfile
中。
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0' # or platform :osx, '10.10' if your target is OS X.
use_frameworks!
pod 'Swinject', '2.8.3'
pod 'SwinjectAutoregistration', '2.8.3'
然后运行pod install
命令。有关CocoaPods的安装和使用详情,请访问其官方网站。
Swift Package Manager
在Package.swift
中添加以下内容
dependencies: [
.package(url: "https://github.com/Swinject/SwinjectAutoregistration.git", from: "2.8.3")
],
targets: [
.target(
name: "MyProject",
dependencies: [..., "SwinjectAutoregistration"]
)
...
]
注册
以下是一个自动注册宠物主的简单示例
let container = Container()
container.register(Animal.self) { _ in Cat(name: "Mimi") } // Regular register method
container.autoregister(Person.self, initializer: PetOwner.init) // Autoregistration
其中PetOwner看起来像这样
class PetOwner: Person {
let pet: Animal
init(pet: Animal) {
self.pet = pet
}
}
autoregister
函数接收了PetOwner
初始化器init(pet:Animal)
。从其签名中,Swinject知道它需要一个依赖项Animal
,并从容器中解析它。不需要其他东西。
自动注册在注册具有许多依赖项的服务时特别有用。比较自动注册代码
container.autoregister(MyService.self, initializer: MyService.init)
与纯Swinject中的等效代码
container.register(MyService.self) { r in
MyService(dependencyA: r.resolve(DependencyA.self)!, dependencyB: r.resolve(DependencyB.self)!, dependencyC: r.resolve(DependencyC.self)!, dependencyD: r.resolve(DependencyD.self)!)
}
另一个优点是,如果在开发过程中添加更多依赖项,注册代码不需要重写。
带有名称的注册
服务也可以赋予一个名称,与常规注册方法相同。
container.autoregister(Person.self, name: "johnny", initializer: PetOwner.init)
参数
您还可以为具有动态参数的服务使用自动注册。需要将宠物主人的名字作为参数传递的宠物主人定义如下
class PetOwner: Person {
let name: String
let pet: Animal
init(name: String, pet: Animal) {
self.name = name
self.pet = pet
}
}
并注册如下
container.autoregister(Person.self, argument: String.self, initializer: PetOwner.init)
Swinject 会将具有 String
类型参数的 Person
进行注册。当调用 container.resolve(Person.self, argument: "Michael")
时,Swinject 不会尝试解析 String
作为依赖项,而是将 "Michael" 作为名字传递。
要传递宠物作为参数,您可以调用
container.autoregister(Person.self, arguments: String.self, Animal.self, initializer: PetOwner.init)
//or
container.autoregister(Person.self, arguments: Animal.self, String.self, initializer: PetOwner.init)
参数列出的顺序可以互换。自动注册无法用于更多同类型的参数和/或依赖项。
这是怎样的魔法?
好奇它是如何工作的?自动注册广泛使用泛型。用于注册具有两个依赖项服务的函数类似于以下内容
public func autoregister<Service, A, B>(_ service: Service.Type, initializer: (A, B) -> Service) -> ServiceEntry<Service> {
return self.register(service.self, factory: { r in
return initializer(r.resolve(A.self)!, r.resolve(B.self)!)
} as (ResolverType) -> Service)
}
初始化器是一个函数,就像其他任何函数一样。通过将其作为参数传递,可以推断其依赖项为 (A, B)
并自动解决。这些函数为多达 20 个依赖项生成。有关更多详细信息,请查阅 代码。
运算符
此扩展还旨在减少样板代码数量,同时提高注册代码的可读性。因此,引入了运算符 ~>
。
Petowner(pet: r~>)
// equivalent to
Petowner(pet: r.resolve(Animal.self)!)
依赖项再次从初始化器中的类型推断出来。要指定一个具体类,您可以使用
Petowner(pet: r ~> Cat.self)
要使用命名服务
Petowner(pet: r ~> (Cat.self, name: "mimi"))
或传递参数/
Petowner(pet: r ~> (Cat.self, argument: "Mimi"))
Petowner(pet: r ~> (Cat.self, arguments: ("Mimi", UIColor.black)))
限制
如果一个服务有多个初始化器,Swift 编译器无法确定应该使用哪个,你将得到一个 歧义性的init(x: y: z:)
的问题。这种情况也可能发生在该服务扩展了另一个具有相同参数个数的初始化器的类时。
解决方案是像这样指定初始化器
container.autoregister(Person.self, initializer: PetOwner.init(name:pet:))
自动注册 不能与在初始化器中具有命名的依赖项一起使用。从初始化器中无法获取依赖项的名称。例如,以下代码无法自动注册
container.register(Animal.self, name: "mimi") { _ in Cat(name: "Mimi") }
container.register(Animal.self, name: "charles") { _ in Cat(name: "Charles") }
container.register(Person.self) {
PetOwner(pet: r.resolve(Animal.self, name: "mimi")
}
Swift 5.3
自 Swift 5.3 以来,在推断具有具有默认值的变量的结构体的初始化器时,编译器的行为不同。
struct Cat {
let height: Int = 50
}
编译器将生成两个 init 函数
Cat.init
和 Cat.init(height:)
自 Swift 5.3 以来,以下注册
container.autoregister(Animal.self, initializer: Cat.init)
将尝试使用 Cat.init(height:)
,然后因 未解决的 servcie: Int 初始化器: (Int) -> Animal
而失败
解决方案是让编译器使用无参数的 init
container.autoregister(Animal.self, initializer: Cat.init as () -> Cat)
维护者
制作新版本
我们的发布流程在 Makefile 中描述。运行 make help
命令获取更多信息。
致谢
SwinjectAutoregistration 泛型受到
许可
MIT 许可。有关详细信息,请参阅 LICENSE 文件。