Swift 在super.init中引用self

Swift 在super.init中引用self,swift,closures,swift4,initializer,Swift,Closures,Swift4,Initializer,我有以下代码(编辑:更新代码,以便每个人都可以编译并查看它): 每次我实例化TextAlertView,它都会在super.init上崩溃,访问错误。但是,如果我改变: Action(title: "Yes", { [weak self] in //use self in here.. }) 致: 它起作用了 在超级初始化过程中,是否有方法引用self(在上面的例子中,我在super.init的参数中引用它,无论它是否在操作块内部) 代码编译..它只是在运行时随机崩溃。简短回答: im

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

每次我实例化
TextAlertView
,它都会在
super.init
上崩溃,访问错误。但是,如果我改变:

Action(title: "Yes", { [weak self] in
    //use self in here..
})
致:

它起作用了

在超级初始化过程中,是否有方法引用
self
(在上面的例子中,我在
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
    }
}
super.init
返回之前,您不能捕获并使用
self
作为值。
在您的情况下,您试图将
self
传递给
super.init
作为参数

至于第二部分为什么工作,仅仅是因为没有在其中使用
self
,它不会捕获
self
,因此它不会使用
self
作为值

如果您不想在闭包中使用
self
,那么您就不必担心那里的
strong
/
引用,因为那里根本没有对
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)
}

用参考资料和材料回答更长的问题

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
    }
}
根据:

安全检查4

在初始化的第一阶段完成之前,初始值设定项不能调用任何实例方法、读取任何实例属性的值或将self作为值引用

来自同一文件:

第一阶段

对类调用指定的或方便的初始值设定项

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

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

指定的初始值设定项交给超类初始值设定项,以对其自己存储的属性执行相同的任务

这将继续沿着类继承链向上,直到到达链的顶部

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

因此,只有在调用
super.init
之后,才允许使用
self
作为值:

第二阶段

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

最后,链中任何方便的初始值设定项都可以选择自定义实例并使用
self

现在,当您尝试在闭包的捕获列表中使用
self
作为值时,它会崩溃,这一点我一点也不感到惊讶。更令人惊讶的是,编译器确实允许您这样做—现在我猜这是一种边缘情况,没有实现错误处理

在第二种情况下:

Action(title: "Yes", {
    //Blank.. doesn't reference `self` in any way (weak, unowned, etc)
})
您没有真正捕获
self
,这就是为什么它被允许并且可以工作。但是您没有访问那里的
self
的权限。尝试在那里添加一些使用
self
的代码,编译器会抱怨:


因此,最后,如果你想在闭包中使用
self
,你必须找到一种方法,首先调用
super.init
,然后才将
self
捕获闭包添加到属性中。

这可能是因为动作是一个结构吗?@MikeTaverne;我只是尝试将其设置为
class
,而不是
struct
…同样的问题。我更新了代码,以便我们可以通过游乐场或常规应用程序编译并查看。这是一个非常可怕的错误,在调用
super.init
之前,你不应该捕获
self
——它在最新的Swift 4.1快照中得到了修复,不过,你会在调用'super.init'之前得到预期的“'self'”错误。我知道为什么第二个案例有效(根据代码中的注释)。我没有意识到的是,第一个案例块中的捕获是不允许的,或者为什么编译器允许它编译。@Brandon嗯..然后我的假设是错误的:D..不管怎样,现在你know@Milan我是指你的评论:“尝试添加一些使用self的代码,编译器会抱怨。”我想当编译器抱怨时,这意味着代码无法编译。@MikeTaverne哦..注意OP没有在那里使用
self
,这就是为什么它是为他编译的..我添加了一个屏幕截图,显示xcode确实complains@MikeTaverne对不起,误会了