通过属性包装器崩溃的嵌套依赖注入

Posted

技术标签:

【中文标题】通过属性包装器崩溃的嵌套依赖注入【英文标题】:Nested dependency injection through Property wrapper crashes 【发布时间】:2020-04-20 06:44:34 【问题描述】:

关注this 一切正常。

但是当我尝试用同样的方法解决嵌套依赖项时(依赖注入的类又具有依赖项——在我们的例子中是 NetworkService),它崩溃了。 我在这里做错了什么?任何帮助将不胜感激。

实时场景

class AppContainer 
    static let shared = AppContainer()

    var index: [Any] = [NetworkingLibrary(), NetworkService()]

    func resolve<T>(_ type: T.Type) -> T 
        return index.first(where:  $0 as? T != nil ) as! T
    


@propertyWrapper
struct Inject<Value> 
    var value: Value

    var wrappedValue: Value 
        get 
            return value
        
        set 
            value = newValue
        
    

    init(_ container: AppContainer = AppContainer.shared) 
        value = container.resolve(Value.self)
    


class UserService 
    @Inject() var networkLayer: NetworkLayer

    init()  

    func fetchUsers() 
        networkLayer.fetchData()
    


class PostService 
    @Inject() var networkLayer: NetworkLayer

    init()  

    func fetchPosts() 
        networkLayer.fetchData()
    


protocol NetworkLayer 
    func fetchData()


class NetworkService: NetworkLayer 
    @Inject() var networkLibrary: NetworkingLibraryProtocol //Expecting networkLibrary to be resolved to NetworkingLibrary()
    func fetchData() 
        networkLibrary.fetch()
        print("Fetching Data")
    



let postService = PostService() // crashes here
postService.fetchPosts()

let userService = UserService() // crashes here
userService.fetchUsers()

可以在操场上运行的代码

class AppContainer 
    static let shared = AppContainer()

    var index: [Any] = ["***", NetworkService()]

    func resolve<T>(_ type: T.Type) -> T 
        return index.first(where:  $0 as? T != nil ) as! T
    


@propertyWrapper
struct Inject<Value> 
    var value: Value

    var wrappedValue: Value 
        get 
            return value
        
        set 
            value = newValue
        
    

    init(_ container: AppContainer = AppContainer.shared) 
        value = container.resolve(Value.self)
    


class UserService 
    @Inject() var networkLayer: NetworkLayer

    init()  

    func fetchUsers() 
        networkLayer.fetchData()
    


class PostService 
    @Inject() var networkLayer: NetworkLayer

    init()  

    func fetchPosts() 
        networkLayer.fetchData()
    


protocol NetworkLayer 
    func fetchData()


class NetworkService: NetworkLayer 
    @Inject() var str: String // Expecting str to be resolved to "***"
    func fetchData() 
        print(str)
        print("Fetching Data")
    



let postService = PostService()
postService.fetchPosts()

let userService = UserService()
userService.fetchUsers()

崩溃日志:-

错误:执行被中断,原因:EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0)。 进程一直停留在中断点,使用“thread return -x”返回表达式求值前的状态。

【问题讨论】:

【参考方案1】:

我还在使用属性包装器在 Swift 5.1 中搜索 Dependency Injection

这对我有用 - 它与您的解决方案非常相似,并且非常适合嵌套依赖项:

enum Dependencies 
    struct Name: Equatable 
        let rawValue: String
        static let `default` = Name(rawValue: "__default__")
        static func == (lhs: Name, rhs: Name) -> Bool  lhs.rawValue == rhs.rawValue 
    

    final class Container 
        private var dependencies: [(key: Dependencies.Name, value: Any)] = []

        static let `default` = Container()

        func register(_ dependency: Any, for key: Dependencies.Name = .default) 
            dependencies.append((key: key, value: dependency))
        

        func resolve<T>(_ key: Dependencies.Name = .default) -> T 
            return (dependencies
                .filter  (dependencyTuple) -> Bool in
                    dependencyTuple.key == key
                        && dependencyTuple.value is T
                
                .first)?.value as! T
        
    

    @propertyWrapper
    struct Inject<T> 
        private let dependencyName: Name
        private let container: Container
        var wrappedValue: T  container.resolve(dependencyName) 

        init(_ dependencyName: Name = .default, on container: Container = .default) 
            self.dependencyName = dependencyName
            self.container = container
        
    

这就是您在代码中使用它的方式:

Dependencies.Container.default.register(NetworkService())
Dependencies.Container.default.register(TransactionRepository())
Dependencies.Container.default.register(TransactionService())

class TransactionService 
    @Dependencies.Inject() private var networkService: NetworkService
    @Dependencies.Inject() private var transactionRepository: TransactionRepository


class SomeOtherService 
    @Dependencies.Inject() private var transactionService: TransactionService

完整的解释可以在here找到。

【讨论】:

以上是关于通过属性包装器崩溃的嵌套依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Angular 中的依赖注入将属性指令实例传递给嵌套组件

如何通过依赖注入传递 Web API 请求?

在装饰器中使用全局嵌套模块

如何从嵌套类通过依赖注入服务进行访问?

依赖注入递归组件的用法

属性包装器到属性包装器