我将首先解释一下我在尝试做的事情以及在遇到问题之前我是如何陷入困境的。
作为我自己的学习练习,我解决了一些我已经在Objective-C中解决的问题,看看我如何用Swift来解决它们。我坚持的具体情况是一小部分,它在更改之前和之后捕获值,并在两者之间进行插值以创建动画的关键帧。
为此,我有一个对象Capture
,其中包含对象的属性,关键路径以及前后值的两个id
属性。稍后,在插入捕获的值时,我确保可以通过将它们中的每一个包装在Value
类中进行插值,该类使用类集群根据其包装的值的类型返回适当的类,或{{ 1}}用于不支持的类型。
这很有效,而且我能够在Swift中使用相同的模式,但它并不像Swift那样。
我没有将捕获的值包装为启用插值的方法,而是创建了一个nil
协议,类型可以符合并使用协议扩展,以便类型支持必要的基本算法:
Mixable
这部分工作得非常好,并且强制实现了均匀混合(一种类型只能与自己的类型混合),而在Objective-C实现中并没有强制执行。
下一个逻辑步骤,这就是我遇到困难的地方,似乎是让每个Capture实例(现在是一个struct)保存两个相同可混合类型的变量,而不是两个protocol SimpleArithmeticType {
func +(lhs: Self, right: Self) -> Self
func *(lhs: Self, amount: Double) -> Self
}
protocol Mixable {
func mix(with other: Self, by amount: Double) -> Self
}
extension Mixable where Self: SimpleArithmeticType {
func mix(with other: Self, by amount: Double) -> Self {
return self * (1.0 - amount) + other * amount
}
}
。我还将初始化器参数从一个对象和一个关键路径更改为一个返回对象的闭包AnyObject
()->T
这可以在推断出类型时起作用,例如:
struct Capture<T: Mixable> {
typealias Evaluation = () -> T
let eval: Evaluation
let before: T
var after: T {
return eval()
}
init(eval: Evaluation) {
self.eval = eval
self.before = eval()
}
}
但不是键值编码,返回AnyObject:\
let captureInt = Capture {
return 3.0
}
// > Capture<Double>
错误:无法为类型&#39; Capture&#39;调用初始值设定项使用类型&#39;(() - &gt; _)&#39;
的参数列表
let captureAnyObject = Capture {
return myObject.valueForKeyPath("opacity")!
}
不符合AnyObject
协议,因此我可以理解为什么这不起作用。但我可以检查对象的实际类型,因为我只覆盖少数可混合类型,但我可以覆盖所有情况并返回正确类型的Capture。太看如果这甚至可以工作我做了一个更简单的例子
Mixable
在保证类型推断时有效:
struct Foo<T> {
let x: T
init(eval: ()->T) {
x = eval()
}
}
但是当闭包可以返回不同的类型时
let fooInt = Foo {
return 3
}
// > Foo<Int>
let fooDouble = Foo {
return 3.0
}
// > Foo<Double>
错误:无法调用类型&#39; Foo&#39;的初始化程序。使用类型&#39;(() - &gt; _)&#39;
的参数列表
我甚至无法单独定义这样的闭包。
let condition = true
let foo = Foo {
if condition {
return 3
} else {
return 3.0
}
}
错误:无法在当前上下文中推断闭包类型
这是否可以完成?可以使用条件来确定泛型的类型吗?或者是否有另一种方法来保存两个相同类型的变量,其中类型是根据条件决定的?
我真正想要的是:
但在解决这个问题时,似乎这个方向是死路一条。我将不得不采取一些步骤并尝试不同的方法(如果再次卡住,可能会问一个不同的问题)。
答案 0 :(得分:4)
如Twitter所述,必须在编译时知道类型。然而,对于问题末尾的简单示例,您可以明确键入
let evaluation: Foo<Double> = { ... }
它会起作用。
因此,在Capture
和valueForKeyPath:
恕我直言的情况下,您应该将值(或安全地或强制转换)投射到您期望值为Mixable
类型的值,并且应该工作正常。毕竟,我不确定valueForKeyPath:
应该根据条件返回不同的类型。
您希望返回两种完全不同的类型(在Int
和Double
的简单情况下无法隐式投放)的确切情况是什么?评估结束?
答案 1 :(得分:0)
在我的完整示例中,我还有CGPoint,CGSize,CGRect,CATransform3D的案例
由于Swift的严格打字,这些限制正如您所说的那样。所有类型必须在编译时明确知道,并且每个类型只能是一种类型 - 甚至是泛型(它通过在编译时被称为的方式来解析)。因此,你唯一能做的就是把你的类型变成一个类似于Objective-C本身的伞形类型:
let condition = true
let evaluation = {
() -> NSObject in // *
if condition {
return 3
} else {
return NSValue(CGPoint:CGPointMake(0,1))
}
}