Swift 为什么我需要强制向下转换来处理具有约束的协议阵列

Swift 为什么我需要强制向下转换来处理具有约束的协议阵列,swift,protocols,Swift,Protocols,这是我的代码: import UIKit protocol Test where Self: UIView {} class MyView: UIView, Test { } let array: [Test] = [MyView()] let view = array[0] let myAlpha = (view as UIView).alpha //ERROR 错误是: 错误:“测试”不能转换为“UIView”;你是想用 “作为!”迫使沮丧 为什么原力下降?该协议只能由UIVi

这是我的代码:

import UIKit

protocol Test where Self: UIView {}

class MyView: UIView, Test {

}

let array: [Test] = [MyView()]

let view = array[0]

let myAlpha = (view as UIView).alpha //ERROR
错误是:

错误:“测试”不能转换为“UIView”;你是想用 “作为!”迫使沮丧

为什么原力下降?该协议只能由UIView采用,因此数组
array
中的每个元素都是-A
UIView
,对吗?是否有任何情况下它不是一个
UIView
,因此需要向下转换?

更新-在Swift5(Xcode11.1)中修复


由于您将数组定义为包含
Test
对象,因此

let array: [Test] // <-- This

因此,如果您知道此对象的类型为
UIView
(这也是因为您告诉编译器它是
Test
对象),那么您需要强制强制转换

如果将
数组的类型更改为
[MyView]
则不需要强制强制转换:

import UIKit

protocol Test where Self: UIView {}

class MyView: UIView, Test {}

let array: [MyView] = [MyView()]

let view = array[0]

let myAlpha = view.alpha

我挠头了,我知道我自己问题的答案。如果只有类,这种情况永远不会发生,但如果使用协议(以及Java中的接口),我实际上可以在数组中放入另一个类,如下所示:

import UIKit

protocol Test where Self: UIView {}

class MyView: UIView, Test {}

var array: [Test] = [MyView()]

let view = array[0]

class NotAUIView {}

let myFakeTestInstance = NotAUIView() as! Test

array.append(myFakeTestInstance) // Auch! Compiles but it does not conform to the protocol Test!!

它可以编译,但在运行时崩溃。子类当然可以采用协议测试,我并没有考虑过。强制向下转换到协议总是编译到任何类而没有错误,但是强制向下转换到其他永远不会工作的类,会从编译器发出警告,说它将始终失败。

我猜编译器不会将类型约束(尽管可能很严格)识别为实际继承

考虑到在协议中,类的标识是已知的,您可以通过扩展协议使其自身执行类型转换来解决这个问题

@objc protocol Test where Self:UIView {}

extension Test
{
   var asUIView: UIView { return self }
}

view.asUIView.alpha // will work


请注意,您需要将@objc添加到您的协议声明中,这样才能使用本机Swift类不需要的桥接类(UIView)。

每个测试实例都必须是UIView的实例。为什么需要悲观?不幸的是,我认为编译器不够聪明,无法理解这一点。不,斯威夫特已经出局多年了。。。编译器应该知道协议是受限的,我相信。。。如果有人有答案,我将等待:)每个测试实例都必须是UIView的实例。为什么需要沮丧?编译器应该知道,每个测试元素都是UIVIEW,只是因为你通过协议看到了逻辑连接,编译器不是因为数组的声明。是的,哈哈,我在这里没有遇到编译器错误,但这显然不是问题所在。您需要强制向下广播,因为您的阵列类型错误。在这种情况下,编译器如何知道它可以将类型为
Test
的对象
view
强制转换为
UIView
?因为协议声明是
protocoltest,其中Self:UIView{}
。因此,只有
UIView
s应该能够
Test
s。这就是我要问的问题,问题是为什么我需要将一个受约束的协议向下转换为一个它被约束到的类型,它只是不能以这种方式工作。使用
protocoltest where Self:UIView{}
可以定义
Test
只能由
UIView
使用,但是
Test
仍然不能从
UIView
继承,因此编译器无法强制转换对象。我对标题中的“为什么”问题没有答案-我同意你的最后一段。我猜对Swift类型系统了解更多的人可以解释为什么不是这样。但是,您可以定义
typealias TestView=UIView&Test
,然后
let array:[TestView]=[MyView()]
,您不会得到错误。@Connor Yea这真的很奇怪,它确实是关于协议的,我从来没有在类/子类中得到这种奇怪的行为。。。这可能是一个解决方案,但我正在试图找到一种方法,它如何崩溃,因为斯威夫特真的希望我对子句
where Self:UIView{}进行降级
意味着只有
UIView
的实例和子类可以采用协议并实现协议要求——这在编译时进行评估——但这并不意味着
Test
对象必须是
UIView
实例。@vadian是的,我没有考虑过。我在这里添加了一个答案:)谢谢值得注意的是,在编译器支持这一点之前,您可以使用带下划线的协议和类存在类型别名来解决它:FYI-
myFakeTestInstance
由于强制转换而属于类型
Test
。所以你的评论是不正确的。这个答案并没有真正解决您的问题,因为您向编译器撒谎,声称
notaiview
实际上是一个
测试。它应该编译。它在运行时使用
notaiview()时崩溃!测试
因为它显然不是一个真正的测试
@rmaddy首先我很困惑为什么我需要向下转换,因为如果它只使用类,所以没有协议,你就不需要强制向下转换。由于以后任何类都可以采用该协议,因此不能保证数组只包含UIView元素。你的评论是对的,我会改变你编辑的评论仍然是错误的。行
将myFakeTestInstance=NotAUIView()设为!Test
相同,让MyFaketTestInstance:Test=NotAUIView()为!测试
import UIKit

protocol Test where Self: UIView {}

class MyView: UIView, Test {}

var array: [Test] = [MyView()]

let view = array[0]

class NotAUIView {}

let myFakeTestInstance = NotAUIView() as! Test

array.append(myFakeTestInstance) // Auch! Compiles but it does not conform to the protocol Test!!
@objc protocol Test where Self:UIView {}

extension Test
{
   var asUIView: UIView { return self }
}

view.asUIView.alpha // will work