如何要求一个协议只能被特定的类采用

100
我希望你能够翻译这个协议:
protocol AddsMoreCommands {
     /* ... */
}

只能被从类UIViewController继承的类所采纳。 这个页面告诉我可以通过编写来指定它仅被类(而不是结构)采用。

protocol AddsMoreCommands: class {
}

但我无法看出如何要求它只被特定的类采用。该页面后面谈到了在协议扩展中添加where子句以检查一致性,但我也不知道如何适应。

extension AddsMoreCommands where /* what */ {
}

有没有办法实现这个?谢谢!

4个回答

122
protocol AddsMoreCommands: class {
    // Code
}

extension AddsMoreCommands where Self: UIViewController {
    // Code
}

4
我差点就做对了...我写成了 self 而不是 Self :-( 非常感谢,现在可以了! - emrys57
3
如果您需要在协议中包含属性,那么这种方式就行不通了,因为扩展无法包含存储属性。 - shim
1
它可以具有存储的属性,您只需使用以下代码:objc_getAssociatedObject(self, &KeyName) as? PropertyType - Michał Ziobro
1
文档已更改为使用 AnyObject 而不是 class -> https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID281。但目前两个版本的行为都相同,没有弃用警告。 - heyfrank
Swift语法中不能添加重写,这只是事实吗?例如:override var prefersStatusBarHidden - Chris Prince
显示剩余2条评论

92

不使用扩展程序也可以实现这一点:

protocol AddsMoreCommands: UIViewController {
    // code
}

这与下面的内容完全相同:

protocol AddsMoreCommands where Self: UIViewController {
   // code
}

我通常使用第一种选项,如果约束条件是Self,那么根据Swift文档中的推荐方式来实现。如果约束条件是其他诸如关联类型,则可以使用where

请注意,由于UIViewController也是一个class,因此此协议可用于实现弱引用属性(例如委托)。

已编辑2021/01/05:之前发布的解决方案有警告,我已将其删除并使用此解决方案,它不会产生任何警告。


6
我觉得这很巧合,我刚刚点击了一个两年前的问题,结果发现有个完美的解决方案就在一小时前发布。 - Oscar Apeland
Xcode 9.1 现在警告我此事:冗余布局约束 'Self' : 'AnyObject'。布局约束 'Self' : 'AnyObject' 在此处被暗示。将我的代码更改为所接受答案的格式似乎更好。 - Zig
3
自 Xcode 9.1 开始,仅类协议现在使用 AnyObject 而不是 classprotocol AddsMoreCommands: AnyObject where Self: UIViewController { // 代码 } - dodgio
是的,警告仍然显示。这是关于此问题的Swift bug。编译器在进行协议时会抱怨(晦涩难懂),因为代码在协议上指定了“AnyObject”两次。该bug列出了为什么这是一个不好的警告。 - dodgio
1
@DávidPásztor 您是正确的,但是如果您想在委托等结构模式中使用它,并使属性弱引用,那么必须显式地添加“class” :) - rgkobashi
显示剩余6条评论

51
因为之前回答中出现了问题,我最终得到了这个声明:

protocol AddsMoreCommands where Self : UIViewController { 
    // protocol stuff here  
}

Xcode 9.1没有警告


5
如果我理解有误,请纠正我,但与上述解决方案相比(在Xcode 9.1及以上版本中生成警告),问题在于无法将代理声明为弱引用? - Kyle Goslan
此外,当我在Swift 4.1中使用此解决方案时,我需要将AddsMoreCommands的属性转换为UIViewController,而我想避免这种情况... - heyfrank
9
为了避免类型转换,你可以这样做:typealias AddsMoreCommandsViewController = UIViewController & AddsMoreCommands - plu

39

现在在Swift 5中,您可以通过以下方式实现:

protocol AddsMoreCommands: UIViewController {
     /* ... */
}

相当方便。


这两者有什么区别?extension AddsMoreCommands where Self: UIViewController { // Code }和protocol AddsMoreCommands: UIViewController { /* ... */ }提前感谢您。 - Rahul Patel
我认为你应该使用“协议”而不是“扩展”。如果是这样的话,有人已经回答了。点击这里查看 - rgkobashi

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