我有以下代码:
import UIKit
protocol Fooable: class where Self: UIViewController {
func foo()
}
class SampleViewController: UIViewController, Fooable {
func foo() {
print("foo")
}
}
let vc1: Fooable = SampleViewController()
let vc2: Fooable = SampleViewController()
// vc1.show(vc2, sender: nil) - error: Value of type 'Fooable' has no member 'show'
// (vc1 as! UIViewController).show(vc2, sender: nil) - error: Cannot convert value of type 'Fooable' to expected argument type 'UIViewController'
(vc1 as! UIViewController).show((vc2 as! UIViewController), sender: nil)
Run Code Online (Sandbox Code Playgroud)
注释行不编译.
为什么UIViewController即使Fooable协议需要,我也被迫将协议类型对象强制转换为符合它的类型继承UIViewController?
采用该协议Fooable告诉编译器这个特定的UIViewController响应foo(),不能再多了.
在相反的结论,Fooable并没有成为UIViewController必然.
如果受影响的类不是,那么约束Self: UIViewController只是编译器在编译时抱怨的另一个信息UIViewController
在您的情况下,注释SampleViewController到Fooable编译器只知道SampleViewController响应foo().它不知道该类型实际上是一个子类UIViewController.
因此,如果要访问具体类的属性,请不要为协议注释具体类.
但是,您可以将show方法和其他常见属性/方法添加到协议中
protocol Fooable: class where Self: UIViewController {
func foo()
func show(_ vc: Fooable, sender: Any?)
}
Run Code Online (Sandbox Code Playgroud)
那么你可以使用,Fooable因为编译器知道采用协议的类型响应方法.
将类型注释为协议的合适做法是,例如,当您要创建异构但受限制的集合类型时
let array : [CustomStringConvertible] = ["Foo", 1, false]
array.forEach{ print("\($0)")}
Run Code Online (Sandbox Code Playgroud)
代码使用description所有项目响应的属性打印三个项目.编译器可识别的三个项目如其中有一个类型的description财产,还不如String,Int和Bool.
更新:
在Swift 5中,实现了对超类约束协议的支持.
在斯威夫特4.x中,斯威夫特并不完全支持超制约的协议,也就是说,可以定义protocol P where Self : C在那里C是一个类的类型.
正如Swift编译工程师Slava Pestov所说,编译器在实际实现该功能之前不会阻止您这样做的事实是疏忽.
Slava Pestov添加了评论 - 2018年5月31日下午1:19
[...]"协议P:Foo,其中Self:Class"是由用户意外发现的,并不完全正常.这是一个疏忽,它没有被禁止.
然而,这是一个功能,旨在作为SE-0156的一部分在该语言的未来版本中完全实现.
Slava Pestov添加了评论 - 2018年5月31日下午1:19
两者都应该有效,但我们尚未完全实施该提案.
(编辑:Slava现在已经在#17611,#17651,#17816和#17851中实现了这一点,所以你将在Swift 5中获得它们 - 你可以同时尝试主快照或Xcode 10.2 beta)
一旦实现,您将能够将这样的协议类型视为要求符合类型继承的类类型(例如,允许您将其Fooable视为UIViewController无需转换),就像您可以处理的一样类存在诸如Fooable & UIViewController作为一个UIViewController.
不仅如此,您还可以直接在协议上而不是在where子句中声明超类要求,例如:
protocol Fooable : UIViewController {
func foo()
}
Run Code Online (Sandbox Code Playgroud)
然而,在Swift 5之前,我建议转向远远超出超类约束协议 - 它们目前在它们周围有一些令人讨厌的粗糙边缘.
例如,这将在运行时在Swift 4.1中错误编译和崩溃:
class C : P {
func speak() {}
}
protocol P where Self : C {
func speak()
}
let c: P = C()
c.speak()
Run Code Online (Sandbox Code Playgroud)
作为一种变通方法,您可以使用带有类存在类型的强调协议来强制执行类约束.例如:
import UIKit
protocol _Fooable : class {
func foo()
}
typealias Fooable = _Fooable & UIViewController
class SampleViewController : Fooable /* implicitly : UIViewController */ {
func foo() {
print("foo")
}
}
// ...
let vc1: Fooable = SampleViewController()
let vc2: Fooable = SampleViewController()
vc1.show(vc2, sender: nil)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
657 次 |
| 最近记录: |