请考虑以下代码,该代码会向视图添加手势识别器。
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
(在手势识别器的初始化程序中),这是因为Swift的两个 - 阶段初始化。
我对轻松修复这项工作并不感兴趣,但这引发了一些问题:
1)如果self
未准备好使用,为什么编译器允许我们在这里使用self
?如果我过早地尝试使用self
,我不应该收到编译器错误吗?
2)我们无法在XCode中使用alt +单击直接检查self
的类型。但是,我们可以检查我的即席变量test1
和test2
的类型。虽然test2
的类型为ViewController
,但正如预期的那样,test1
的类型为(ViewController) -> () -> ViewController
(即,一个带有ViewController
的闭包并返回一个闭包什么都不需要,并返回ViewController
)。那是什么以及为什么self
在同一个类中有两种不同的类型?
答案 0 :(得分:4)
1)
如果我过早地尝试使用self
,我是否会遇到编译错误?
我同意。您可以发送bug report to swift.org。
如果self
尚未准备好使用,为什么编译器允许我们在此使用self
?
不幸的是,self
,the method self()
of NSObject
的后代还有另一个NSObject
。
2)
这是什么?为什么自我在同一个班级中有两种不同的类型?
当前的Swift解释class
上下文中的初始值表达式,而不是实例上下文。
您知道方法名称可以在Swift中用作闭包:
class ViewController: UIViewController {
//..
func aMethod() {
//...
}
func anInstanceMethod() {
let meth = aMethod // () -> ()
}
}
Swift还可以引用class
上下文中的实例方法,该方法生成所谓的未应用方法引用(请参阅SE-0042),该引用当前返回一个curried函数:
class ViewController: UIViewController {
//...
func aMethod() {
//...
}
class func aClassMethod() {
let meth = aMethod // (ViewController) -> () -> ()
}
}
方法self()
。
通常我们不需要self()
方法,我认为应该改变这种行为。
答案 1 :(得分:2)
这是一种适用于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)的答案。
如果我们在 Class 上引用此方法returnSelf
,我们会获得签名(NonNSObject) -> () -> NonNSObject
。您可以使用任何实例方法执行此操作:
let test = NonNSObject.returnSelf
签名在这种情况下有意义:
let curriedFunction = NonNSObject.returnSelf // (Self) -> () -> Self
let readyToCall = curriedFunction(NonNSObject()) // () -> Self
let finallyApplied = readyToCall() // Self
将所有部分放在一起,我们可以看到,在ViewController的情况下(从UIViewController继承链的哪个方向继承自NSObject),有一个实例方法self
,编译器假设你的意思,所以它使用它而不是实例本身(因为这将是一个错误)。因此,它的签名是在类本身上使用实例方法的自然结果 - 它需要一个实例,这是第一个参数。
总结:
1)Swift编译器在NSObject上找不到函数self
而不是假设你犯了错误,并返回curried表单。
2)这是函数的curry形式,特别是返回其自身类型的实例方法。
2.5)它仍然以粉红色突出显示,因为Swift-ObjC互操作是温和的hacky,self
既是一种方法又是self
。
作为奖励,结构根本不能引用自己,甚至懒惰。