在协议中使用 self in 函数

Using self in function in a protocol

我有这个协议:

从故事板实例化一个 ViewController:

protocol Storyboarded {
    static func instantiate() -> Self
}

extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {

        // this pulls out "MyApp.MyViewController"
        let fullName = NSStringFromClass(self)

        // this splits by the dot and uses everything after, giving "MyViewController"
        let className = fullName.components(separatedBy: ".")[1]

        // load our storyboard
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

        // instantiate a view controller with that identifier, and force cast as the type that was requested
        return storyboard.instantiateViewController(withIdentifier: className) as! Self
    }
}

一个将依赖项注入到 Viewcontrollers 中:

protocol DependencyInjection where Self: UIViewController {
    associatedtype myType: DependencyVC
    func injectDependencys(dependency: myType)
}

现在我想添加另一个,这样我就可以从依赖本身创建 ViewController:

protocol DependencyVC {
    associatedtype myType: DependencyInjectionVC & Storyboarded
    func createVC() -> myType
}


extension DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self {
        let viewController = T.instantiate()
        viewController.injectDependencys(dependency: self)
        return viewController
    }

}

但我自己得到这个错误:

Cannot invoke 'injectDependencys' with an argument list of type '(dependency: Self)'

这是我拥有的 DependencyClass:

class TopFlopDependency: DependencyVC {
    typealias myType = TopFlopVC


    var topFlopState: TopFlopState

    lazy var topFlopConfig: TopFlopConfig = {
        let SIBM = StatIntervalBaseModel(stat: "ppc", interval: "24h", base: "usd")
        return TopFlopConfig(group: Groups.large, base: "usd", valueOne: SIBM)
    }()

    init(state: TopFlopState) {
        self.topFlopState = state
    }

    func createVC() -> TopFlopVC {
        let topflopVC = TopFlopVC.instantiate()
        topflopVC.injectDependencys(dependency: self)

        let viewController: TopFlopVC = makeVC()

        return topflopVC
    }
}

我在使用 makeVC 时遇到这个错误:

'TopFlopDependency' requires the types 'TopFlopDependency.myType' and 'TopFlopDependency.myType' (aka 'TopFlopVC') be equivalent to use 'makeVC'

其他解决方案:

protocol DependencyVC {   
}
extension DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self {
        let viewController = T.instantiate()
        viewController.injectDependencys(dependency: self)
        return viewController
    }
}

尝试使用时:

let viewController: TopFlopVC = makeVC()

我收到无法推断 T 的错误。

为什么我不能这样做?你有解决方案吗?

谢谢!

当您调用 viewController.injectDependencys(dependency: self) 时,已知 self 属于 DependencyVC 的某个子类型。但是,DependencyInjectionassociatedtype myType: DependencyVC 只是说符合 DependencyInjection 的类型将使用 some 类型 myType (符合DependencyVC)。所以不能保证它的实际类型是 myType.

的子类型

associatedtypes 与泛型类型参数的工作方式并不完全相同,因为 associatedtypes 在 "defining" 类型时给出,而泛型类型参数在 "using"一种。

归根结底,你可能不想associatedtype myType,而是直接DependencyVC

更新

鉴于您提供的额外信息,我认为这将是最佳解决方案:

protocol DependencyInjection where Self: UIViewController {
    func injectDependency(_ dependency: DependencyVC)
}

protocol DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>() -> T
}

extension DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>() -> T {
        let viewController = T.instantiate()
        viewController.injectDependency(self)
        return viewController
    }
}

如您所见,我冒昧地将 injectDependencys(dependency: DependencyVC) 重命名为 injectDependency(_ dependency: DependencyVC),因为您只注入了一个依赖项,而 dependency: 标签并没有真正添加任何内容在呼叫站点。

无论如何,这允许您使用您的依赖项创建视图控制器的实例。假设您将依赖项存储在一个名为 dependency 的变量中,那么您可以通过 let topFlopVC: TopFlopVC = dependency.makeVC()

从它创建一个视图控制器

您需要添加另一个约束条件。您的 DependencyInjection 协议需要非常特定类型的 DependencyVC (myType)。但是您的 DependencyVC 扩展适用于任何 DependencyVC。所以你需要用 where 子句将 T 的 myType 限制为相同的类型:func createVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self

所以一个完整的例子是这样的:

protocol Storyboarded {
    static func instantiate() -> Self
}

extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {
        ...
    }
}

protocol DependencyVC {
}

protocol DependencyInjection where Self: UIViewController {
    associatedtype myType: DependencyVC
    func injectDependencys(dependency: myType)
}

extension DependencyVC {
    func makeVC<T: Storyboarded & DependencyInjection>(type _: T.Type? = nil) -> T where T.myType == Self {
        let viewController = T.instantiate()
        viewController.injectDependencys(dependency: self)
        return viewController
    }
}

struct MyDependency: DependencyVC {}

class MyVC: UIViewController, Storyboarded, DependencyInjection {
    func injectDependencys(dependency: MyDependency) {
        print(dependency)
    }
}