在super.init中引用self

问题描述 投票:4回答:1

我有以下代码(编辑:更新代码,以便每个人都可以编译并查看):

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的参数中做它?

代码编译..它只是在运行时随机崩溃。

swift closures swift4 initializer
1个回答
6
投票

简短回答:

self返回之前,您无法捕获并使用super.init作为值。在你的情况下,你试图将self“传递”给super.init作为参数。

根据为什么第二部分起作用,仅仅因为没有使用self,它不捕获self,因此它不使用self作为值。

如果你不想在闭包中使用self,那么你不需要担心那里的strong / weak引用,因为那里根本没有引用self(因为它没有被捕获)。没有保留周期的危险。


关于“使用self作为值”的简短说明 - 您可以在赋值的左侧使用self来引用self初始化时的属性:

let myProperty: String

init(with myProperty: String) {
    // this usage of self is allowed
    self.myProperty = myProperty
    super.init(nibName: nil, bundle: nil)
}

更长的答案与参考和东西:

根据documentation

安全检查4

初始化程序无法调用任何实例方法,读取任何实例属性的值,或者在初始化的第一阶段完成之后将self作为值引用。

第一阶段的初始化是通过调用super.init来结束的

从相同的文档:

阶段1

在类上调用指定或便利初始化程序。

分配该类的新实例的内存。内存尚未初始化。

该类的指定初始值设定项确认该类引入的所有存储属性都具有值。现在初始化这些存储属性的内存。

指定的初始化程序移交给超类初始化程序,以便为其自己的存储属性执行相同的任务。

这继续了类继承链,直到到达链的顶部。

一旦达到链的顶部,并且链中的最后一个类确保其所有存储的属性都具有值,则认为实例的内存已完全初始化,并且阶段1已完成。

所以只有在调用super.init之后才允许使用self作为值:

阶段2

从链的顶部向下工作,链中的每个指定初始化程序都可以选择进一步自定义实例。初始化程序现在可以访问self并可以修改其属性,调用其实例方法等。

最后,链中的任何便利初始化器都可以选择自定义实例并使用self

现在,当你尝试使用self作为闭包捕获列表中的值时,我并不感到惊讶,它崩溃了。我更惊讶的是编译器允许你这样做 - 现在我想这是一个没有实现错误处理的边缘情况。

在第二种情况:

Action(title: "Yes", {
    //Blank.. doesn't reference `self` in any way (weak, unowned, etc)
})

你并没有真正捕获self,这就是为什么它被允许并且它有效。但是你无法访问那里的self。尝试添加一些使用self的代码,编译器会抱怨:

enter image description here

所以最后,如果你想在闭包中使用self,你将不得不找到一种方法如何首先调用super.init,然后才添加self捕获属性的闭包。

© www.soinside.com 2019 - 2024. All rights reserved.