在协议中使用自助功能

时间:2018-11-22 14:27:09

标签: swift generics protocols

我有以下协议:

一个从Storyboard实例化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
    }

}

但我自己会遇到此错误:

  

无法使用类型为参数的列表调用“ injectDependencys”   '(依赖:自我)'

这是我拥有的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'需要类型'TopFlopDependency.myType'和   “ TopFlopDependency.myType”(也称为“ TopFlopVC”)等同于使用   '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的错误。

为什么我不能这样做?您有解决方案,我将如何使其正常工作?

谢谢!

2 个答案:

答案 0 :(得分:1)

调用viewController.injectDependencys(dependency: self)时,已知selfDependencyVC的某种子类型。但是,DependencyInjection的{​​{1}}只是说,符合associatedtype myType: DependencyVC的类型将对DependencyInjection使用 some 类型(符合{{1 }})。因此,不能保证其实际类型将是myType的子类型。

DependencyVC与通用类型参数的工作方式不太相同,因为在“定义”类型时给出myType,而在“使用”类型时给出通用类型参数。

这全都归结为您可能可能不想拥有associatedtype,而是直接服用associatedtype

更新

鉴于您提供的其他信息,我相信这将是最好的解决方案:

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:

从中创建视图控制器

答案 1 :(得分:1)

您需要添加另一个约束。您的DependencyInjection协议需要一种非常特定的DependencyVC(myType)类型。但是您的DependencyVC扩展可与任何DependencyVC一起使用。因此,您需要使用where子句将T的myType约束为相同类型:YouTube

因此,完整的示例如下:

func createVC<T: Storyboarded & DependencyInjection>() -> T where T.myType == Self