gpt4 book ai didi

swift - 如何避免我的自定义依赖项注入(inject)工具出现保留周期?

转载 作者:行者123 更新时间:2023-12-05 05:42:08 26 4
gpt4 key购买 nike

我创建了一个自定义 propertyWrapper 以在代码中注入(inject)我的依赖项,因此在测试代码时,我可以使用指向对象的 WritableKeyPath 链接传递一个模拟内存。

这就是我在生产代码中使用它的方式。这非常方便,因为我不需要在初始化程序中传递对象。

@Injected(\.child) var child

这就是我在单元测试中使用它来代替 WritableKeyPath 传递模拟的方式。

let parentMock = ParentMock()
InjectedDependency[\.parent] = parentMock

问题是,在我尝试使用它的代码的某些部分,当 Child 类需要访问 Parent 循环中的类。当我在 Playground 中查找并使用它时,我可能已经注意到当彼此链接时创建了两个对象,并且当将变量设置为 时,每个对象只有一个释放而不是两个无

如何更新我的 @propertyWrapper 或可以对该解决方案进行哪些改进以使其按预期工作?为什么创建两个对象而不是它们引用内存中的对象?

所以下面设置了这个自定义依赖注入(inject)工具在代码中的使用。我已经使用 weak var parent: Parent? 实现了经典方法来释放内存中的对象,没有任何问题来展示我的预期。

protocol ParentProtocol {}
class Parent: ParentProtocol {

//var child: Child?
@Injected(\.child) var child

init() { print("🔔 Allocating Parent in memory") }
deinit { print ("♻️ Deallocating Parent from memory") }
}

protocol ChildProtocol {}
class Child: ChildProtocol {

//weak var parent: Parent?
@Injected(\.parent) var parent

init() { print("🔔 Allocating Child in memory") }
deinit { print("♻️ Deallocating Child from memory") }
}

var mary: Parent? = Parent()
var tom: Child? = Child()

mary?.child = tom!
tom?.parent = mary!

// When settings the Parent and Child to nil,
// both are expected to be deallocating.
mary = .none
tom = .none

这是使用自定义依赖注入(inject)解决方案时日志中的响应。

🔔 Allocating Parent in memory
🔔 Allocating Child in memory
🔔 Allocating Child in memory // Does not appear when using the weak reference.
♻️ Deallocating Child from memory
🔔 Allocating Parent in memory // Does not appear when using the weak reference.
♻️ Deallocating Parent from memory

这是我的自定义 PropertyWrapper 的实现,用于处理 ParentChild 键之后的依赖注入(inject),例如使用。

// The key protocol for the @propertyWrapper initialization.
protocol InjectedKeyProtocol {
associatedtype Value
static var currentValue: Self.Value { get set }
}

// The main dependency injection custom tool.
@propertyWrapper
struct Injected<T> {

private let keyPath: WritableKeyPath<InjectedDependency, T>

var wrappedValue: T {
get { InjectedDependency[keyPath] }
set { InjectedDependency[keyPath] = newValue }
}

init(_ keyPath: WritableKeyPath<InjectedDependency, T>) {
self.keyPath = keyPath
}
}

// The custom tool to use in unit tests to implement the mock
// within the associated WritableKeyPath.
struct InjectedDependency {

private static var current = InjectedDependency()

static subscript<K>(key: K.Type) -> K.Value where K: InjectedKeyProtocol {
get { key.currentValue }
set { key.currentValue = newValue }
}

static subscript<T>(_ keyPath: WritableKeyPath<InjectedDependency, T>) -> T {
get { current[keyPath: keyPath] }
set { current[keyPath: keyPath] = newValue }
}
}

// The Parent and Child keys to access the object in memory.
extension InjectedDependency {
var parent: ParentProtocol {
get { Self[ParentKey.self] }
set { Self[ParentKey.self] = newValue }
}

var child: ChildProtocol {
get { Self[ChildKey.self] }
set { Self[ChildKey.self] = newValue }
}
}

// The instantiation of the value linked to the key.
struct ParentKey: InjectedKeyProtocol {
static var currentValue: ParentProtocol = Parent()
}

struct ChildKey: InjectedKeyProtocol {
static var currentValue: ChildProtocol = Child()
}

最佳答案

很多变化,所以只是比较 - 通常我们需要考虑引用计数,即。谁保留引用...因此它仅适用于引用类型。

使用 Xcode 13.3/iOS 15.4 测试

protocol ParentProtocol: AnyObject {}
class Parent: ParentProtocol {

//var child: Child?
@Injected(\.child) var child

init() { print("🔔 Allocating Parent in memory") }
deinit { print ("♻️ Deallocating Parent from memory") }
}

protocol ChildProtocol: AnyObject {}
class Child: ChildProtocol {

//weak var parent: Parent?
@Injected(\.parent) var parent

init() { print("🔔 Allocating Child in memory") }
deinit { print("♻️ Deallocating Child from memory") }
}

protocol InjectedKeyProtocol {
associatedtype Value
static var currentValue: Self.Value? { get set }
}

// The main dependency injection custom tool.
@propertyWrapper
struct Injected<T> {

private let keyPath: WritableKeyPath<InjectedDependency, T?>

var wrappedValue: T? {
get { InjectedDependency[keyPath] }
set { InjectedDependency[keyPath] = newValue }
}

init(_ keyPath: WritableKeyPath<InjectedDependency, T?>) {
self.keyPath = keyPath
}
}

// The custom tool to use in unit tests to implement the mock
// within the associated WritableKeyPath.
struct InjectedDependency {

private static var current = InjectedDependency()

static subscript<K>(key: K.Type) -> K.Value? where K: InjectedKeyProtocol {
get { key.currentValue }
set { key.currentValue = newValue }
}

static subscript<T>(_ keyPath: WritableKeyPath<InjectedDependency, T?>) -> T? {
get { current[keyPath: keyPath] }
set { current[keyPath: keyPath] = newValue }
}
}

// The Parent and Child keys to access the object in memory.
extension InjectedDependency {
var parent: ParentProtocol? {
get { Self[ParentKey.self] }
set { Self[ParentKey.self] = newValue }
}

var child: ChildProtocol? {
get { Self[ChildKey.self] }
set { Self[ChildKey.self] = newValue }
}
}

// The instantiation of the value linked to the key.
struct ParentKey: InjectedKeyProtocol {
static weak var currentValue: ParentProtocol?
}

struct ChildKey: InjectedKeyProtocol {
static weak var currentValue: ChildProtocol?
}

测试代码的输出:

enter image description here

Complete test module in project is here

关于swift - 如何避免我的自定义依赖项注入(inject)工具出现保留周期?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72118404/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com