Swift中的自我类型及其在两阶段初始化中的使用
请考虑下面的代码,它将一个手势识别器添加到视图中。Swift中的自我类型及其在两阶段初始化中的使用
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
let gesture = UITapGestureRecognizer(target: self, action: #selector(handleGesture(gesture:)))
let test1 = self
@objc func handleGesture(gesture: UITapGestureRecognizer) {
// some code
print("hello")
}
override func viewDidLoad() {
let test2 = self
super.viewDidLoad()
imageView.addGestureRecognizer(gesture)
}
}
按照this question,上面的代码不工作,因为我想用self
(在手势识别器的初始化)时不能完全初始化了,这是因为斯威夫特的两相初始化。
我不感兴趣,容易修复,使这项工作,但是这会触发几个问题:
1)为什么编译器还允许我们在这里使用self
如果self
还没有准备好使用?如果我试图尽快使用self
,我应该不会收到编译器错误?
2)我们不能直接检查self
的类型与alt +点击XCode。但是,我们可以检查我的特设变量test1
和test2
的类型。 test2
的类型为ViewController
,正如预期的那样,test1
的类型为(ViewController) ->() -> ViewController
(即,需要ViewController
并返回无效并返回ViewController
的闭包的闭包)。那是什么以及为什么self
在同一个班级中有两种不同的类型?
1)
我不应该得到,如果我试图过早地使用self
编译错误?
我同意。您可以发送bug report to swift.org。
为什么编译器允许我们在这里使用self
如果self
还没有准备好使用?
不幸的是,还有另一种self
在NSObject
,the method self()
of NSObject
后裔。
2)
那是什么?为什么自己有同一类的两种不同类型的?
当前Swift将在class
上下文中解释初始值表达式,而不是在实例上下文中。
你知道方法名可以用夫特用作封闭:
class ViewController: UIViewController {
//..
func aMethod() {
//...
}
func anInstanceMethod() {
let meth = aMethod //() ->()
}
}
夫特也可以指一个实例方法在class
上下文,其产生一个所谓的未应用方法参考(见SE-0042),其目前返回咖喱功能:
class ViewController: UIViewController {
//...
func aMethod() {
//...
}
class func aClassMethod() {
let meth = aMethod // (ViewController) ->() ->()
}
}
方法self()
也是如此。
通常我们不需要self()
方法,我认为应该改变这种行为。
这是适用于Objective-C对象的有趣行为。让我们以这三个例子:
class Object: NSObject {
let test = self // compiles
}
class NonNSObject {
// let test = self // errors
lazy var lazyTest = self // compiles
}
struct NonClass {
// let test = self // errors
lazy var lazyTest = self // errors
}
NonNSObject
展品你会逃避什么:
对象不能引用自身,直到其完全初始化,let
绑定都必须完全初始化之前进行初始化,所以这个失败。
但是,NSObject碰巧有一个Objective-C方法- (instancetype)self;
,它返回self。我们可以在NonNSObject上对此进行建模:
func returnSelf() -> NonNSObject {
return self
}
这就是我们开始看到2)的答案的地方。
如果我们在类别上参考此方法returnSelf
,我们得到签名(NonNSObject) ->() -> NonNSObject
。
let test = NonNSObject.returnSelf
签名很有意义在这种情况下:
- 的说法是,我们实际上要调用的方法上
- 的对象,你可以用任何实例方法,这样做然后我们“应用”函数(在这种情况下没有参数)
- 然后我们终于得到我们的返回值
let curriedFunction = NonNSObject.returnSelf // (Self) ->() -> Self let readyToCall = curriedFunction(NonNSObject()) //() -> Self let finallyApplied = readyToCall() // Self
把所有的拼在一起,我们可以看到,在视图控制器的情况下(从UIViewController中继承其一路从NSObject的链条继承)有一个实例方法self
,编译器是假设你的意思是,所以它使用它而不是实例本身(因为这将是一个错误)。因此它的签名是在类本身上使用实例方法的自然结果 - 它需要一个实例,这是第一个参数。
总结:
1)代替假设你犯了一个错误的,迅捷编译器发现上NSObject的一个函数self
并返回咖喱形式。 2)这是一个函数的curried形式,特别是返回它自己类型的实例方法。 2.5)它仍然以粉红色突出显示,因为Swift-ObjC interop是轻度hacky,而self
既是一种方法,也是self
。
作为奖励,结构不能自动引用,甚至是懒惰。