Xcode报错:Cannot use instance member ‘name’ within property initializer; property initializers run before ‘self’ is available
Xcode报错:Cannot use instance member ‘name’ within property initializer; property initializers run before ‘self’ is available

Xcode报错:Cannot use instance member ‘name’ within property initializer; property initializers run before ‘self’ is available

问题描述

在尝试输出字符串首字母时,下面为示例代码:

var name = "Paul"
var firstLetter = name[name.startIndex]
var body: some View {
    Text("\(firstLetter)")
}

发现存在下面的报错,

Cannot use instance member 'name' within property initializer; property initializers run before 'self' is available

翻译为:“无法在属性初始化程序中使用实例成员“name”;属性初始化程序在“self”可用之前运行。”

经排查发现,问题的原因在于firstLetter属性在初始化时

var firstLetter = name[name.startIndex]

试图访问name,但是name还没有被完全初始化。

可以简单的理解为,Swift不允许在初始化变量时,去引用另一个属性。

尽管name已经声明并分配了一个”Paul”的值。

解决方案

可通过以下两种方法来确保name完全初始化:

1、在init()方法中,初始化firstLetter变量。

struct ContentView: View {
    var name = "Paul"
    var firstLetter: Character
    
    init() {
        self.firstLetter = name[name.startIndex]  // 在初始化方法中,name 已经被初始化,可以安全使用
    }

    var body: some View {
        Text("\(firstLetter)")
    }
}

init()方法是Swift初始化对象的核心步骤,当使用init方法时,你可以先初始化name,然后在name初始化完成后,安全的访问并计算firstLetter。

2、使用计算属性,每次访问时动态计算。

struct ContentView: View {
    var name = "Paul"
    var firstLetter: Character {
        name[name.startIndex]
    }

    var body: some View {
        Text("\(firstLetter)")
    }
}

计算属性不会存储值,因为计算属性只有在被访问时,才会执行代码。

在Swift初始化完成之后,计算属性才会尝试访问其他存储属性,这也意味着它不会在self没有初始化完成时求值,避免了在初始化时访问其他属性的问题。

我们可以通过init()和计算属性,完成初始化时访问其他属性的问题,但是,我们可能还有一个疑问,就是这里的初始化操作全部放在init()中执行,是否可行?

下面就是对于init()方法的延续分析。

init()方法延续分析

struct ContentView: View {
    var name: String
    var firstLetter: Character

    init() {
        self.name = "Paul"  // 先初始化 name
        self.firstLetter = name[name.startIndex]  // 然后再用 name 计算 firstLetter
    }
    var body: some View {
        Text("\(firstLetter)")
    }
}

当我们将name和firstLetter的初始化步骤全部放在init()中执行,就不会存在本次问题报错。

这是因为,如果你在没有使用init()的情况下,Swift在初始化属性时有一个严格的规则,就是在任何属性初始化之前,不能依赖为初始化的属性。

而init()的作用就是允许你按照你的顺序来初始化全部属性。

这里,需要了解一下init方法的特殊性,它允许逐步初始化属性,并且在方法当中,你可以确定某个属性被赋值,并依赖它进行下一步的计算。

问题总结

总而言之,就是当初始化使用init()时,我们可以明确控制属性的初始化顺序,并且可以依赖其他的初始化属性。

如果不使用init(),属性就不能保证初始化的顺序,以及不可以依赖其他的初始化属性。

这是Swift的安全机制,避免在属性未完全初始化时,进行不安全的引用。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注