在 super.init 中引用自己
Referencing self in super.init
我有以下代码(编辑:更新了代码以便每个人都可以编译并查看):
import UIKit
struct Action
{
let text: String
let handler: (() -> Void)?
}
class AlertView : UIView
{
init(actions: [Action]) {
super.init(frame: .zero)
for action in actions {
// let actionButton = ActionButton(type: .custom)
// actionButton.title = action.title
// actionButton.handler = action.handler
// addSubview(actionButton)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class TextAlertView : AlertView
{
init() {
super.init(actions: [
Action(text: "No", handler: nil),
Action(text: "Yes", handler: { [weak self] in
//use self in here..
})
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class MyViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let alert = TextAlertView()
view.addSubview(alert)
self.view = view
}
}
每次我实例化 TextAlertView
时,它都会因访问错误而在 super.init
上崩溃。但是,如果我更改:
Action(title: "Yes", { [weak self] in
//use self in here..
})
至:
Action(title: "Yes", {
//Blank.. doesn't reference `self` in any way (weak, unowned, etc)
})
有效!
有没有办法在超级初始化期间引用 self
是否在动作块内(在上面我在 super.init
的参数中这样做?
代码编译..它只是在运行时随机崩溃。
简答:
您不能在 super.init
returns 之前捕获并使用 self
作为值。 在您的情况下,您正试图“通过" self
到 super.init
作为参数。
至于为什么第二部分有效,只是因为没有在其中使用 self
,它不会捕获 self
,因此它不会使用 self
作为值。
如果你不想在闭包中使用self
,那么你不用担心那里的strong
/weak
引用,因为没有引用到 self
那里(因为它没有被捕获)。没有保留循环的危险。
关于“使用 self
作为值”的简短旁注 - 您可以在赋值的 left-hand 端使用 self
来引用 self
的属性初始化它们时:
let myProperty: String
init(with myProperty: String) {
// this usage of self is allowed
self.myProperty = myProperty
super.init(nibName: nil, bundle: nil)
}
带有参考文献和内容的较长答案:
Safety check 4
An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.
初始化的第一阶段通过调用 super.init
结束,当
来自同一文档:
Phase 1
A designated or convenience initializer is called on a class.
Memory for a new instance of that class is allocated. The memory is not yet initialized.
A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
This continues up the class inheritance chain until the top of the chain is reached.
Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.
所以只有在调用 super.init
之后,您才可以使用 self
作为值:
Phase 2
Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self and can modify its properties, call its instance methods, and so on.
Finally, any convenience initializers in the chain have the option to customize the instance and to work with self
.
现在,当您尝试将 self
用作闭包捕获列表中的值时,它会崩溃,我一点都不感到惊讶。我更惊讶的是编译器确实允许你这样做 - 现在我猜这是一个没有实现错误处理的边缘情况。
第二种情况:
Action(title: "Yes", {
//Blank.. doesn't reference `self` in any way (weak, unowned, etc)
})
您并没有真正捕获 self
,这就是它被允许并且有效的原因。但是您无权访问那里的 self
。尝试在其中添加一些使用 self
的代码,编译器会报错:
所以最后,如果你想在闭包中使用 self
,你必须找到一种方法如何先调用 super.init
,然后才添加 self
捕获属性的闭包。
我有以下代码(编辑:更新了代码以便每个人都可以编译并查看):
import UIKit
struct Action
{
let text: String
let handler: (() -> Void)?
}
class AlertView : UIView
{
init(actions: [Action]) {
super.init(frame: .zero)
for action in actions {
// let actionButton = ActionButton(type: .custom)
// actionButton.title = action.title
// actionButton.handler = action.handler
// addSubview(actionButton)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class TextAlertView : AlertView
{
init() {
super.init(actions: [
Action(text: "No", handler: nil),
Action(text: "Yes", handler: { [weak self] in
//use self in here..
})
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class MyViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let alert = TextAlertView()
view.addSubview(alert)
self.view = view
}
}
每次我实例化 TextAlertView
时,它都会因访问错误而在 super.init
上崩溃。但是,如果我更改:
Action(title: "Yes", { [weak self] in
//use self in here..
})
至:
Action(title: "Yes", {
//Blank.. doesn't reference `self` in any way (weak, unowned, etc)
})
有效!
有没有办法在超级初始化期间引用 self
是否在动作块内(在上面我在 super.init
的参数中这样做?
代码编译..它只是在运行时随机崩溃。
简答:
您不能在 super.init
returns 之前捕获并使用 self
作为值。 在您的情况下,您正试图“通过" self
到 super.init
作为参数。
至于为什么第二部分有效,只是因为没有在其中使用 self
,它不会捕获 self
,因此它不会使用 self
作为值。
如果你不想在闭包中使用self
,那么你不用担心那里的strong
/weak
引用,因为没有引用到 self
那里(因为它没有被捕获)。没有保留循环的危险。
关于“使用 self
作为值”的简短旁注 - 您可以在赋值的 left-hand 端使用 self
来引用 self
的属性初始化它们时:
let myProperty: String
init(with myProperty: String) {
// this usage of self is allowed
self.myProperty = myProperty
super.init(nibName: nil, bundle: nil)
}
带有参考文献和内容的较长答案:
Safety check 4
An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.
初始化的第一阶段通过调用 super.init
结束,当
来自同一文档:
Phase 1
A designated or convenience initializer is called on a class.
Memory for a new instance of that class is allocated. The memory is not yet initialized.
A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
This continues up the class inheritance chain until the top of the chain is reached.
Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.
所以只有在调用 super.init
之后,您才可以使用 self
作为值:
Phase 2
Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self and can modify its properties, call its instance methods, and so on.
Finally, any convenience initializers in the chain have the option to customize the instance and to work with
self
.
现在,当您尝试将 self
用作闭包捕获列表中的值时,它会崩溃,我一点都不感到惊讶。我更惊讶的是编译器确实允许你这样做 - 现在我猜这是一个没有实现错误处理的边缘情况。
第二种情况:
Action(title: "Yes", {
//Blank.. doesn't reference `self` in any way (weak, unowned, etc)
})
您并没有真正捕获 self
,这就是它被允许并且有效的原因。但是您无权访问那里的 self
。尝试在其中添加一些使用 self
的代码,编译器会报错:
所以最后,如果你想在闭包中使用 self
,你必须找到一种方法如何先调用 super.init
,然后才添加 self
捕获属性的闭包。