Ios 为什么在使用闭包初始化属性时可以使用self?
官方文件(Swift 4.1)说: 如果使用闭包初始化属性,请记住 在 关闭被执行。这意味着您无法访问任何其他文件 闭包中的属性值,即使这些属性 具有默认值。您也不能使用隐式self属性, 或者调用实例的任何方法 所以我想我需要使用lazy var,今天我创建了一个按钮Ios 为什么在使用闭包初始化属性时可以使用self?,ios,swift,Ios,Swift,官方文件(Swift 4.1)说: 如果使用闭包初始化属性,请记住 在 关闭被执行。这意味着您无法访问任何其他文件 闭包中的属性值,即使这些属性 具有默认值。您也不能使用隐式self属性, 或者调用实例的任何方法 所以我想我需要使用lazy var,今天我创建了一个按钮 let loginRegisterButton: UIButton = { let button = UIButton(type: .system) button.backgroundColor = UIColo
let loginRegisterButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.rgb(red: 80, green: 101, blue: 161)
button.setTitle("Register", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(UIColor.white, for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
button.addTarget(self , action: #selector(handleRegister), for: .touchUpInside)
return button
}()
所以我只使用了self-in-addTarget方法,XCode似乎并没有打扰到更多的程序并没有出现任何错误。一切都进行得很好。那么,当我用闭包初始化属性时,为什么我可以使用self呢?是不是有些变化或者我错过了什么 代码中的目标可能是
nil
,这可能有效的原因是:
…如果指定nil,UIKit将在响应程序链中搜索
对象,该对象响应指定的操作消息并传递
向该对象发送消息
(摘自)
您可以通过在控制器初始化期间或之后设置断点,并通过检查按钮的\u targetActions
数组(在调试器的变量视图中)(您可以看到target
的地址是0x0
)来验证目标实际上是nil
在您的示例中,
loginRegisterButton
是在控制器初始化期间设置的,这意味着还没有self
(换句话说,您不能保证所有实例变量都在此时初始化)。lazy
解决这个问题的方法是在第一次访问时延迟实际分配。奇怪的是,闭包中的self
计算为函数对象。(Hamish在评论中解释说,该函数是NSObjectProtocol.self
的一种通用形式)但是,当您稍后询问按钮的目标时,它会报告+[NSNull null]
:
let loginRegisterButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = .red
button.setTitle("Register", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(UIColor.white, for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
print(self)
// Output: (Function)
button.addTarget(self, action: #selector(handleRegister), for: .touchUpInside)
let target = button.allTargets.first!.base
print(target, type(of: target))
// Output: <null> NSNull
return button
}()
let loginRegisterButton:ui按钮={
let button=ui按钮(类型:。系统)
button.backgroundColor=.red
button.setTitle(“寄存器”,用于:。正常)
button.translatesAutoresizingMaskIntoConstraints=false
button.setTitleColor(UIColor.white,用于:。正常)
button.titleLabel?.font=UIFont.boldSystemFont(字体大小:16)
打印(自我)
//输出:(功能)
addTarget(self,action:#选择器(HandlerRegister),用于:。touchUpInside)
让target=button.allTargets.first!.base
打印(目标,类型(类型:目标))
//输出:NSNull
返回按钮
}()
(使用Xcode 9.3.1进行测试。)
函数转换为NSNull
,如下所示:函数对象在调用addTarget
之前分配,作为target
参数传递。loginRegisterButton
-创建闭包包含对新分配函数的强引用
该按钮将目标存储为调零弱参考
addTarget
返回后,闭包释放对函数的强引用。这是对函数的唯一有力引用,因此Swift解除了对函数的分配。这会将按钮对函数的弱引用设置为nil
然后,当我们为
allTargets
请求按钮时,它将构造一个NSSet
。由于NSSet
不能直接保持nil,因此按钮将+[NSNull null]
放在集合中。代码中的目标可能是nil
,这可能起作用的原因是:“…如果指定nil,UIKit将在响应程序链中搜索响应指定操作消息并将消息传递给该对象的对象”(来自该方法的文档)我很困惑,你的代码正在使用let
,但在你自己的问题中,你也在谈论lazy var
。那么是哪一个呢?@Honey OP知道lazy var
是解决问题的正确方法。问题是,为什么提供的代码段可以工作,即使他没有使用lazy var
@Honey我刚才使用了不使用lazy var和usedlet@N.Khasanov通过在控制器初始化期间或之后设置断点并检查\u targetActions
数组(在调试器的变量视图中),可以验证目标实际上是nil
在本例中,loginRegisterButton
是在控制器初始化期间设置的,这意味着还没有self
(换句话说,您不能保证此时所有内容都已初始化)lazy
解决这一问题的方法是在第一次访问时延迟实际的分配。self
是NSObjectProtocol
的实例方法,其格式为(self)->(self)->(self)
,因为Swift将实例属性初始化器视为在类型的静态范围内(即,您可以直接引用静态成员。)。如果您在未继承自NSObject
的类中执行此操作,您将在使用self
时遇到错误。有趣的观察结果!另一件与此相关的事情是,如果您执行类似let s=self
的操作,类型推断系统会将类型报告为(MyViewController)->()->MyViewController
aaand@Hamish在我发表评论时已经澄清了这一点……我记得这在以前的Swift版本中给出了一个错误。那么现在发生了什么变化?@robmayoff现在我明白了你的意思并理解了你的答案谢谢!