KOInject
一个简单轻量级的 IoC 容器,支持使用模式和多个参数的注册/解析/销毁模式。它还支持延迟和隔离解析。
特性
- 支持多个泛型参数的 注册/解析/销毁 模式。
- 支持 使用范围注册 - 通过将对象与其范围参数一起注册来声明解析对象的生存期。
- 延迟解析器 - 仅在需要时才对对象进行延迟解析。在需要动态连接而不是持续连接数据库时很有用。
- 隔离解析器 - 隔离使用解析对象,其中对象在隔离操作的开始时自动解析,并在完成时自动销毁。适用于所有类型的事务。
要求
- iOS 11.0+ / macOS 10.13+ / tvOS 11.0+ / watchOS 4.0+
- Xcode 11.0+
- Swift 5.1
安装
KOInject 不包含任何外部依赖。如果您想保持更新,请通过 Cocoapods 安装 KOInject。
CocoaPods
在 Podfile 的目标中添加以下条目
pod 'KOInject', '~> 2.0'
例如
platform :ios, '11.0'
use_frameworks!
target 'Target Name' do
pod 'KOInject', '~> 2.0'
end
运行以下命令以安装 pods
pod install
Swift Package Manager
点击“文件”->“添加包”->输入KOInject 仓库的 URL,然后点击“添加包”。
您可以在 Package.swift 中手动添加依赖,如下所示
let package = Package(
name: "SomePackage",
platforms: [.iOS(.v11)],
products: [
.library(
name: "YourLib",
targets: ["YourLib"]
)
],
// here we declare dependency that we want to add
dependencies: [
.package(
url: "https://github.com/Flawion/KOInject.git",
from: "2.0.0"
)
]
targets: [
// target where we use our dependency
.target(
name: "YourLib",
dependencies: ["KOInject"]
)
]
)
手动
您可以使用 KOInject 手动并按您的喜好进行更改。这是做这件事的简单方法之一。
- 下载仓库。
- 将 KOInject.xcodeproj 和 Sources 文件夹复制到您的项目目录中。
- 在项目浏览器中点击“向 'Your project' 添加文件”->选择 KOInject.xcodeproj. Xcode 将自动将 KOInject 添加为子项目。
- 在项目设置->目标->添加嵌入库->选择 Workspace->KOInject->KOInject.framework.
- 如果您不希望每次改动后都手动构建KOInject,您可以前往“构建阶段”->“依赖项”->“添加”->“选择工作区”->“KOInject”->“KOInject.framework”。
用法
您需要在计划使用该框架的文件顶部添加以下导入。
import KOInject
注册/解析/销毁
- 首先注册您稍后要解析的依赖项。
let container = KOIContainer()
container.register(type: ApiClientProtocol.self) { _ in
ApiClient()
}
container.register(type: DataStorageProtocol.self) { _ in
DataStorage()
}
您可以添加多个参数,注册可以看起来像这样。
container.register(type: DataStorageProtocol.self) { _, rootPath, clearOnDispose in
DataStorage(rootPath: rootPath, clearOnDispose: clearOnDispose)
}
第一个参数是KOIResolverProtocol类型,因此您可以解析其他所需的依赖项来创建新的对象。
container.register(type: GameDetailsViewModelProtocol.self, scope: .separate) { (resolver, game: GameModel) in
GameDetailsViewModel(apiClient: resolver.resolve()!, game: game)
}
container.register(type: GameDetailsViewControllerProtocol.self, scope: .separate) { (resolver, game: GameModel) in
GameDetailsViewController(viewModel: resolver.resolve(arg1: game)!)
}
- 解析依赖项
struct GameModel {
let uuid: String
let name: String
}
// ...
let game = GameModel(uuid: "123-123-123", name: "TestGame")
let gameDetailsViewController: GameDetailsViewControllerProtocol? = container.resolve(arg1: game)
- 当您不再需要时,请销毁注册数据。
container.dispose()
按作用域注册
在注册类型时,您可以传递额外的作用域参数,这将确定解析对象的生存期。
- shared - 默认作用域。对象将在首次解析时创建一次。在下一次解析调用中,将使用相同的对象。
protocol ObjectIdentifiableProtocol {
var id: String { get }
}
final class ObjectIdentifiable: ObjectIdentifiableProtocol, Identifiable {
let id: String = UUID().uuidString
}
// ...
container.register(type: ObjectIdentifiableProtocol.self) { _ in
ObjectIdentifiable()
}
let object: ObjectIdentifiableProtocol? = container.resolve()
let object2: ObjectIdentifiableProtocol? = container.resolve()
print(object?.id == object2?.id) // prints true
- weakShared - 类似于shared,但当没有强引用时,对象将被删除。
container.register(type: ObjectIdentifiableProtocol.self, scope: .weakShared) { _ in
ObjectIdentifiable()
}
var object: ObjectIdentifiableProtocol? = container.resolve()
let objectId = object?.id
var object2: ObjectIdentifiableProtocol? = container.resolve()
let objectId2 = object2?.id
print(objectId == objectId2) // prints true
// remove strong's references
object = nil
object2 = nil
// resolve new object
let object3: ObjectIdentifiableProtocol? = container.resolve()
let objectId3 = object3?.id
print(objectId == objectId3) // prints false
- separate - 每次解析都会返回新的对象。
container.register(type: ObjectIdentifiableProtocol.self, scope: .separate) { _ in
ObjectIdentifiable()
}
let object: ObjectIdentifiableProtocol? = container.resolve()
let object2: ObjectIdentifiableProtocol? = container.resolve()
print(object?.id == object2?.id) // prints false
懒解析
在某些情况下,我们不想让对象持续有效。注入打开的数据流连接不是最佳解决方案。因此,我们可以使用延迟解析器。
// register dependencies
let container = KOIContainer()
container.register(type: DataBaseClientProtocol.self) { (_, rootPath: String) in
DataBaseClient(rootPath: rootPath)
}
container.register(type: ViewModelProtocol.self, scope: .separate) { (resolver, rootPath: String) in
let dataBaseLazyResolver = KOILazyResolverArg1(resolver: resolver, type: DataBaseClientProtocol.self, arg1: rootPath) // create lazy Resolver
return ViewModel(dataBaseLazyResolver: dataBaseLazyResolver) // inject it!
}
// ...
final class ViewModel: ViewModelProtocol {
let dataBaseLazyResolver: KOILazyResolverArg1<DataBaseClientProtocol, String>
init(dataBaseLazyResolver: KOILazyResolverArg1<DataBaseClientProtocol, String>) {
self.dataBaseLazyResolver = dataBaseLazyResolver
}
// ...
func append(data: Data) {
dataBaseLazyResolver.resolve() // resolve object
dataBaseLazyResolver.object!.append(data: data) // use it
dataBaseLazyResolver.dispose() // dispose it
}
}
隔离解析器
当我们使用事务操作时,我们应该对它们的访问权限非常有限。使用隔离解析器,你只能在隔离的操作中使用解析过的对象。隔离解析器是延迟解析器的一种适配器。
// register dependencies
let container = KOIContainer()
container.register(type: UserBillingProtocol.self, scope: .weakShared) { (_, userId: String) in
UserBilling(userId: userId)
}
container.register(type: ViewModelProtocol.self, scope: .separate) { (resolver, userId: String) in
// create lazy and isolated resolvers
let lazyResolver = KOILazyResolverArg1(resolver: resolver, type: UserBillingProtocol.self, arg1: userId)
let isolatedResolver = KOIIsolatedResolver(lazyResolver: lazyResolver)
return ViewModel(userBillingIsolatedResolver: isolatedResolver)
}
// ...
final class ViewModel: ViewModelProtocol {
let userBillingIsolatedResolver: KOIIsolatedResolver<UserBillingProtocol>
init(userBillingIsolatedResolver: KOIIsolatedResolver<UserBillingProtocol>) {
self.userBillingIsolatedResolver = userBillingIsolatedResolver
}
// ...
func add(payment: Payment) {
// resolve object in isolated action
userBillingIsolatedResolver.resolve { userBilling in
userBilling?.add(payment: payment)
}
}
}
许可证
该项目受MIT许可证的许可 - 有关详细信息,请参阅LICENSE文件。