我想从方法中返回符合UIViewController
的{{1}},所以我正在使用方法签名:
MyProtocol
首先我不明白:如果func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {
返回,例如一个必须遵循签名的myMethod
,我必须强行施放它:
MyViewController
我不能简单class MyViewController: UIViewController, MyProtocol
但我需要像这样投出:return MyViewController()
- 为什么这是必要的?
第二件事:我怎样才能在某个地方使用这种方法?我不能简单地说
return MyViewController() as! T
我收到错误
let x = myMethod() as? UIViewController
我怎样才能实现这样的目标?如果我将它投射到Generic parameter 'T' could not be inferred
它可行,但我当然希望避免这种情况。
编辑:示例
MyViewController
好吧,我确实得到了一部分,但为什么需要转换为class MyViewController : UIViewController, MyProtocol {
}
protocol MyProtocol {
}
func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
return MyViewController() as! T // why is the cast necessary?
}
? T
是MyViewController
的子类并且符合协议,因此不需要强制转换,对吧?
答案 0 :(得分:27)
func myMethod<T where T : UIViewController, T : MyProtocol>() -> T
此声明说:存在一个名为myMethod
的函数,以便myMethod
返回一些特定的 T
,其中T
是一个子类型UIViewController
以及MyProtocol
。这并没有说明T
实际上是什么类型,也没有说明只有一个myMethod
。如果有许多类型都是UIViewController
的子类并且符合MyProtocol
,则可以有很多类型。这些类型中的每一个都创建了myMethod
的新版本(实际上是断言myMethod
的新解决方案,这样的函数确实存在)。
这与以下内容不同:
func myMethod() -> UIViewController
说:函数myMethod
返回UIViewController
的任何子类型。
Swift中没有办法表达“任何类型是UIViewController的子类,并且是MyProtocol的子类型。”您只能讨论符合该标准的特定类型。 Swift不能以这种方式组合类和协议;它只是语言的当前限制,而不是深层次的设计问题。
特定与任何是问题所在。有许多函数可以满足您的myMethod
声明。您可以插入符合规则的每个T
都是候选人。所以当你说myMethod()
时,编译器不知道你的意思是哪个T
。
(我打算扩展这个答案,以更少的类型理论提供它,更多“你如何用代码”来表达,但是donnywals已经有了一个很好的版本。)
*致编辑问题*
func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
return MyViewController() as! T // why is the cast necessary?
}
T
是由来电者决定的特定类型。它不是“任何符合的类型”,而是“符合某种特定的具体类型”。考虑你打电话的情况:
let vc: SomeOtherViewController = myMethod()
在这种情况下,T
为SomeOtherViewController
。 MyViewController
不是那种类型,因此您使用as!
演员表所做的事情很危险。
答案 1 :(得分:10)
在这样的方法中,返回T
表示您必须返回T
。如果您返回MyViewController
,则返回类型应为MyViewController
。 T
是一种泛型类型,它采用Swift编译器可以推断的形式。
因此,使用您的方法签名,协议和方法的简单实现可能如下所示。
protocol MyProtocol {
var name: String { get set }
}
func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {
var vc = T()
vc.name = "Hello, world"
return vc
}
所以,考虑一下您的使用示例:
let x = myMethod()
编译器如何知道T
的具体类型是什么?没有任何东西给它一点MyViewController
的暗示。我们唯一知道的是,无论T
是什么,它都应该是MyViewController
或它的子类。它应符合MyProtocol
。但是这并没有提供有关T
应该是什么类型的信息。
编译器可以推断出我们想要T
的唯一地方是通过返回值。 <>
之间的所有代码都是T
允许的约束。 -> T
是唯一可以在约束之外看到T
的地方。因此,如果我们能以某种方式告诉编译器我们希望myMethod
返回什么,我们已经给出了足够的信息来推断T
。
你的类型转换有效,但我同意它不是很漂亮。编译器推断T
的一种更为漂亮的方法就是这样。
let vc: MyViewController = myMethod()
通过指定vc
的类型,编译器了解我们希望myMethod
返回MyViewController
。现在可以推断T
的类型,如果我们返回T
,我们实际上会返回MyViewController
。
答案 2 :(得分:1)
正如评论中的某些人指出的那样,myMethod
没有通用的明显理由。这样做的理由是:(引用您的评论)
我想使用一种UIViewController并符合特定协议的类型;
让呼叫类型为ViewControllerAndMyprotocol
的
我确实有符合此规则的不同类,因此我不想使用特定类型
不过,myMethod
签名已经约束了类型ViewControllerAndMyprotocol
,即,调用者必须接收UIViewController
,而不是任何“符合此规则的不同类”。
具体的类型可能是ViewControllerAndMyprotocol
,包括MyViewController
的灵活性是为什么在声明let x = myMethod()
中存在类型歧义,需要强制转换:let x = myMethod() as? UIViewController
您可以通过更改myMethod
签名来避免转换:
typealias ViewControllerAndMyprotocol = UIViewController & MyProtocol
func myMethod() -> ViewControllerAndMyprotocol {
return MyViewController()
}
语句let x = myMethod()
不需要强制转换,其类型为ViewControllerAndMyprotocol
,它也是UIViewController
。