Swift下行转换和协议变量

6

我有一个包含一个属性的Swift协议:

protocol WireframeProtocol: class
{
    var rootViewController: UIViewController  { get }
}

我接下来有一个类,按照这个协议进行实现:

class MenuWireframe : WireframeProtocol
{
    let rootViewController: UIViewController

    init()
    {
        self.rootViewController = MenuViewController(nibName: "MenuViewController", bundle: nil)
        (self.rootViewController as! MenuViewController).presenter = MenuPresenter(interactor: MenuInteractor())
    }
}

在我的线框图类中,变量实际上是MenuViewController类型的,但必须声明为UIViewController以符合该协议。 我必须使用(self.rootViewController as! MenuViewController)将其强制转换为正确的类,以便能够访问其属性。 在上面的简单示例中,这样做很好,但在更复杂的情况下不太可读。 是否有更好的方法来声明协议变量?
非常感谢!

1
由于您正在使用VIPER,因此您可以/应该将根视图控制器声明为协议(View协议),并在其中定义一个弱属性,该属性将成为Presenter。 - Dániel Nagy
@bennythemink 你有没有检查过我的答案? - Zell B.
@zellb 哈哈,正在检查中,Zellb,一分钟后回复您。 - bennythemink
@DánielNagy,你很棒,认出我正在使用VIPER。顺便说一句,我因此提升了你的评论 :) 我很想知道你对这种模式的看法?因为目前关于它的信息并不是很多。 - bennythemink
@DánielNagy 完全同意。这确实意味着更多的文件,但我喜欢它促使开发人员更多地考虑关注点分离。如果您遇到任何良好的 VIPER 架构项目的代码示例,请务必分享。非常感谢。 - bennythemink
显示剩余4条评论
3个回答

4

是的,有更好的方法,那就是使用通用协议。为了实现这个目的,你需要像这样声明你的协议:

protocol WireframeProtocol{

    typealias RootViewController : UIViewController
    var rootViewController: RootViewController  { get }

}

然后在您的采用类中将根视图控制器类型设置为MenuViewController

class MenuWireframe : WireframeProtocol{

let rootViewController: MenuViewController

    init(){
        self.rootViewController = MenuViewController(nibName: "MenuViewController", bundle: nil)
        self.rootViewController.presenter = MenuPresenter(interactor: MenuInteractor())
    }
}

嗨zellb,那个有效!但我不确定它为什么有效。你能解释一下吗? - bennythemink
1
类型别名为现有类型定义了一个替代名称,因此在这种情况下,我声明了一个新名称,用于从UIViewController继承的所有类型(在行typealias RootViewController : UIViewController中)。之后,我将rootViewController类型设置为我们的新类型,以便rootViewController变量可以保存从继承自UIViewController的类创建的任何值,因此现在您可以直接将其设置为MenuViewController,而无需任何向下转换要求。希望我表达清楚。 - Zell B.

2
如果您可以更改协议声明,请使用@zelb的答案。如果不能更改,则可以执行以下操作:
class MenuWireframe : WireframeProtocol 
{
    var rootViewController: UIViewController {
        return menuViewController
    }

    let menuViewController: MenuViewController!

    init()
    {
        menuViewController = MenuViewController(...)
        //...
    }
}

嗨,谢谢你的建议。我刚刚测试了一下,恐怕它不起作用。首先,我不能在这种方法中使用“let”,其次,如果我想访问其属性,仍然需要将rootViewController强制转换为MenuViewController类型:(但是还是谢谢你的建议。 - bennythemink
@bennythemink 为什么不能使用 "let"?而且,想法是访问 menuViewController 而不是 rootViewController,因为你知道给定的对象实际上是 MenuWireFrame 而不是任意的 WireFrameProtocol - Jakub Vano
嗨,我不能使用“let”,因为在您的建议中rootViewController现在是一个计算属性。也许我有点迂腐,但这意味着我在类中现在有两个属性而不是一个。但再次感谢您的提示。 - bennythemink
@JakubVano 即使按照我的方式更改协议声明,也不会导致任何错误。 - Zell B.

1

你试过这个吗:

protocol WireframeProtocol: UIViewController {
  var rootViewController: UIViewController  { get }
} 

class MenuWireframe : WireframeProtocol
{
    let rootViewController: WireframeProtocol

    init()
    {
        //...
    }
}

嗨,我得到了一个“非类类型WireframeProtocol不能从类UIViewController继承”的错误:/恐怕那样行不通。感谢您的建议。 - bennythemink
是的,我在一个Swift项目中指出了这个缺失。在Objective-C中,我们可以做类似于UIViewController<WireframeProtocol>* rootViewController;的事情... - Vincent Saluzzo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接