特定类的 Swift 协议

8

我希望定义一个变量,它的类型为UIViewController,并符合P1协议。

问题:

  • 我应该修改协议还是v1类型?
  • 我应该如何做到这一点?

代码:

protocol P1 {

    func f1();
}


var v1 : P1 //But needs to be a UIViewController which conforms to `P1`

4个回答

21

在 Swift 4 中,您可以使用新的“&”符号创建符合协议和类的变量,其语法如下:

let vc: UIViewController & P1

13

从Swift 4开始:

多亏了SE-0156

The proposal keeps the existing & syntax but allows one of the elements to be either AnyObject or of class type. The equivalent to the above Objective-C types would look like this:

AnyObject & Protocol1 & Protocol2
Base & Protocol

As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

我们现在可以声明:

var v1 : UIViewController & P1 // UIViewController which conforms to `P1`

Swift 4之前:

没有直接的方法来实现这一点。你必须将v1声明为UIViewControllerP1,然后使用强制转换(as)来获取其他功能。

最接近你想要的是使用泛型。例如:

func testGeneric<T: UIViewController where T: P1>(input: T) {
    input.f1()
}

例如,如果您的情况是要有一个既通过继承控制又通过协议实现成为委托的控制器,则可以按照以下方式进行操作:
protocol Delegate: class { 
    func doDelegate()
}

class Controller { 
    func doController() {
        print("Controller")
    }
}

class ConcreteController: Controller, Delegate {
    func doDelegate() {
        print("Delegate")
    }
}

class View {
    private weak var controller: Controller? = nil
    private weak var delegate: Delegate? = nil
    
    func setDelegateController<T: Controller where T: Delegate>(delegateController: T?) {
        controller = delegateController
        delegate = delegateController
    }
    
    func test() {
        controller?.doController()
        delegate?.doDelegate()
    }
}

并且为了测试此功能:

请参考以下内容:

let view = View()
let controller = ConcreteController()

view.setDelegateController(controller)

view.test()  // This will print:
             // Controller
             // Delegate

你的解决方案看起来很不错,但是有没有一种方法可以声明该类型(泛型类型)的变量。我需要它来进行委托。 - user1046037
是的,在 testGeneric 的函数体中,你可以声明一个 T 类型的变量。 - 0x416e746f6e
我的意思是,不使用testGeneric函数,我能否声明一个类型为T:UIViewController,其中T:P1的变量? - user1046037
没有直接实现这样的方法。不过请看我的编辑。也许对你的情况来说已经足够了。 - 0x416e746f6e
Swift 3 中的 func 格式已更改,现在看起来像这样:func setup<T: Controller>(viewController: T) where T: Delegate - xaphod

1
你可以创建另一个空协议,将UIViewController约束到该协议中,然后使用protocol<>指令将两个协议组合成一个单一的类型:
protocol UIViewControllerType {} // empty
protocol P1 { ... }

// Make sure only UIViewController adopts UIViewControllerType
extension UIViewController: UIViewControllerType {}

extension UITableViewController: P1 {}

// This works:
var v1: protocol<UIViewControllerType, P1> = UITableViewController()

// This doesn't:
var v2: protocol<UIViewControllerType, P1> = UIViewController()

这种方法存在问题。现在我不能使用v1(不转换类型)调用UIViewController方法。我主要是想要这个来进行委托。 - user1046037
1
没错。你可以将想要使用的 UIViewController 方法添加到 UIViewControllerType 协议中。但我同意这不太美观。 - Ole Begemann
那是另一种可能性。没错。 - user1046037

0
这样怎么样:
protocol P1 {
    func f1()
    func getVC() -> UIViewController
}

class MyVC : UIViewController, P1 {

    func f1() {
        // do stuff
    }

    func getVC() -> UIViewController {
        return self
    }
}

var v1 : P1 = MyVC()

v1.getVC() // do UIViewController related things

被标记为已回答的Swift 4解决方案是最优雅的。一旦Swift 4发布,事情会变得更容易。 - user1046037

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