本篇文章中主要涉及Class类的继承部分,会从required构造函数,到super.init()初始化父类部分谈起。全面梳理Class类的继承流程,通过代码样例等方式,学习并理解required关键词、super关键词等、父类和子类的构造函数、初始化等内容部分。
required简介
required 是 Swift 中用于构造函数(初始化方法)的关键字,表示这个构造函数必须在所有子类中实现。也就是说,如果父类的构造函数标记为 required,任何继承该类的子类都必须实现这个构造函数,甚至如果子类没有显式实现,它也会自动继承父类的 required 构造函数。
为什么要使用 required
使用 required 的主要原因是确保在继承链中,特定的初始化逻辑能够被所有子类继承和保持一致。通常在以下情况下会用到:
1、保证子类初始化的一致性:
如果希望所有子类都具有某个特定的初始化方法,或者必须遵循相同的初始化规则,可以在父类中使用 required。
这样,无论子类是否显式实现了这个构造函数,它们都会自动继承。
2、配合协议和 Codable 使用:
在 Codable 的场景中,类需要一个可以从 Decoder 初始化的构造函数 init(from:)。如果希望这个类可以被子类化,并且所有子类都能够使用相同的方式进行解码,那么将 init(from:) 标记为 required 是很有用的。
这样做可以确保即使是子类,也能够通过相同的初始化方法进行解码。
class Animal {
var name: String
// required initializer
required init(name: String) {
self.name = name
}
}
class Dog: Animal {
// Automatically inherits the required initializer from the superclass
}
在上面的代码中,Animal 类有一个 required 构造函数。Dog 继承了 Animal,即使没有显式地定义 init(name:),也自动获得了这个构造函数。
代码示例:
class Animal {
var name: String
// required initializer
required init(name: String) {
self.name = name
}
}
class Dog: Animal {
// Automatically inherits the required initializer from the superclass
}
struct ContentView: View {
let dog = Dog(name: "fangfang")
var body: some View {
Text("\(dog.name)") // 输出 fangfang
}
}
因为Dog继承了Animal,Dog没有显式定义构造方法,就会自动获取父类的构造方法:
// required initializer
required init(name: String) {
self.name = name
}
因此,可以使用Dog(name:)初始化Dog类,并继承Animal的name属性,所以Dog类也可以使用name属性。

举一反三
子类定义构造函数
根据上面的样例代码,如果我们想要Dog子类拥有自己的构造函数。
class Dog: Animal {
var type: String
init(type: String) {
self.type = type
}
}
这时Xcode会提示我们:required初始化程序init(name:)必须由Animal的子类提供。
'required' initializer 'init(name:)' must be provided by subclass of 'Animal'
Insert '
required init(name: String) {
fatalError("init(name:) has not been implemented")
}
'
这个错误的原因是Animal类中定义的init(name:)构造函数被标记为required。这意味着任何继承Animal的子类都必须实现这个required标记的构造函数。如果子类没有显式的提供required init(name:),编译器就会报错。
为什么给子类添加构造函数会报错呢?
这是因为我们在Dog子类中自定义了一个init(type:)构造函数。根据Swift的规则,如果子类定义了自己的构造函数,它就不会自动继承父类的构造函数,包括required构造函数。这就是为什么编译器提示需要显式的提供required init(name:)。

解决方案为:给Dog子类中添加父类的required构造函数。
class Dog: Animal {
var type: String
init(type: String) {
self.type = type
}
required init(name: String) {
self.name = name
}
}

子类初始化父类部分
下面是我们子类初始化父类部分,主要涉及如何通过super.init()调用父类的构造函数。
当我们运行上面的代码,给Dog子类自定义init(type:)构造函数之后,通过required重新实现父类的required构造函数之后,我们的Xcode仍然会报错:
'super.init' isn't called on all paths before returning from initializer
'self' used in property access 'name' before 'super.init' call
这是因为在Swift中,子类在初始化时必须确保父类的属性和状态被正确设置。Swift需要先初始化父类的部分,才能继续完成子类的初始化。这是对象初始化的一般规则,确保整个对象层级的所有部分都能够被正确配置。
父类Animal中,有一个属性name。这个属性是属于Animal的,当创建Dog实例时,Dog不仅有自己的属性(如type),还会继承Animal的属性(如name)。
因为只继承了属性,所以必须通过调用super.init(name:)来初始化它。
super.init(name: name) // 调用父类的初始化方法
如果子类的初始化方法没有调用父类的构造函数,父类的属性就无法被正确初始化,从而导致对象不完整并引发编译错误。
所以,Swift强制要求子类初始化时调用父类的构造函数,确保整个对象层级中每一部分的状态都被妥善处理。

因此,我们需要给Dog子类自定义的构造函数以及父类的required构造函数中,都添加super.init()。
class Dog: Animal {
var type: String
init(name:String, type: String) {
self.type = type
super.init(name: name)
}
required init(name: String) {
self.name = name
super.init(name: name)
}
}
在Dog子类自定义的构造函数中:如果Dog子类定义了一个自定义的构造函数,比如init(name:String, type: String),那么就必须调用super.init(name:name)才能确保Animal的属性name被正确初始化。
因为Animal类中的init(name:)是required,子类Dog必须提供一个实现。所以也需要在这个构造函数中调用super.init(name:name),否则父类的name属性将无法被初始化。
那么是否可以不使用super.init?
答案是不行的,每个子类的构造函数都必须调用super.init,否则编译器就会报错。只有以下几种情况,可以不显式调用super.init:
1、子类没有定义自己的属性:如果子类没有添加任何新属性,并且不需要额外的构造逻辑,那么 Swift 可以自动继承父类的构造函数,不需要手动写 super.init。
2、子类使用默认的构造函数:如果没有在子类中定义任何构造函数,并且父类有默认的构造函数,子类会自动继承它。此时无需显式调用 super.init。
因此,子类的构造函数都必须调用super.init,以确保父类的属性得到正确初始化。
这是Swift确保类继承链中所有属性都能得到完整初始化的一种方式。
初始化顺序
那么,我们的代码现在无问题了么?还是有报错。现在的报错是:
'self' used in property access 'name' before 'super.init' call
Property 'self.type' not initialized at super.init call
这次报错的原因是Dog子类声明的required构造函数没有初始化type,以及在调用super.init之前,使用了self访问name属性。
required init(name: String) {
self.name = name
self.type = "Type" // 在required构造函数中初始化type
super.init(name: name)
}
required构造函数之所以需要初始化type,是因为Swift 要求所有属性在构造函数结束之前必须被初始化。子类的 required 构造函数也需要遵守这个规则。因此,即使是继承自父类的 required 构造函数,子类的属性仍然需要在这个函数中得到初始化。

这两个问题在于,我们如果想要Dog子类使用required构造函数,那么我们也应该给Dog子类的属性进行初始化,因此,在required构造函数中应该添加self.type。
required init(name: String) {
self.name = name
self.type = type
super.init(name: name)
}
我们在添加self.type之后,我们需要type拥有required构造函数的入参。
required init(name: String,type: String) {
self.name = name
self.type = type
super.init(name: name)
}
当我们修改required构造函数,添加type入参时,又会报新的报错:
Invalid redeclaration of 'init(name:type:)'
'required' initializer 'init(name:)' must be provided by subclass of 'Animal'
这个报错在于required初始化程序init(name:)必须由Dog子类提供,required关键字的构造函数必须严格匹配父类定义,所以我们无法任意修改参数列表,让Dog子类进行传参。
所以,Dog子类的type没有默认值,那么我们只能给Dog子类的type进行赋值的方法:
required init(name: String) {
self.name = name
self.type = "Type"
super.init(name: name)
}
赋值之后,就只剩下最后一个报错了:
'self' used in property access 'name' before 'super.init' call
这个报错是在访问或修改继承父类的self属性之前,应该先使用super.init进行父类的初始化。因为当前对象还没有完全初始化,
因此,我们只能先用super.init进行父类的初始化后,再使用self.name进行赋值。此外,因为我们required构造函数就是self.name = name,我们只需要调用父类的构造函数即可。
required init(name: String) {
self.type = "no"
super.init(name: name) // 先调用父类的构造函数
self.name = "haha" // 才能访问或修改父类的self
}
最后,我们的代码就正常运行。
class Animal {
var name: String
// required initializer
required init(name: String) {
self.name = name
}
}
class Dog: Animal {
// Automatically inherits the required initializer from the superclass
var type: String
init(type: String) {
self.type = type
super.init(name: "unknow")
}
required init(name: String) {
self.type = "no"
super.init(name: name) // 先调用父类的构造函数
self.name = "haha" // 才能访问或修改父类的self
}
}
struct ContentView: View {
let dog = Dog(name: "xiaoBlack")
var body: some View {
Text("\(dog.name)") // haha,修改后的name
Text("\(dog.type)") // no
}
}
总结
这里我们了解到required构造函数中,应先初始化子类的属性(type),然后尽早调用super.init(name:),确保子类和父类的所有部分都正确初始化。
此外,在super.init之前,不要访问父类self的属性。
扩展知识
构造器继承的规则总结
子类会自动继承父类构造器的前提:
1、子类没有定义任何自己的指定构造器(例如 init(type:));
2、父类的构造器不是 private 或者 convenience;
3、父类构造器不是 required 的(如果是 required,子类必须显式写出来或继承);
4、父类属性没有 let 且子类没有重写它们。
因此,如果子类声明构造方法,子类就无法自动继承父类的构造方法。
属性继承
子类会自动继承父类的属性(除非自己定义了同名属性);
不能在子类中定义与父类同名的存储属性,会报错;
可以重写父类的计算属性;
一旦重写了同名属性(尝试 shadow(遮蔽)),就不会继承原来的行为。
继承父类属性代码示例:
class Animal {
var name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
var type: String = "Husky"
}
let dog = Dog(name: "Buddy")
print(dog.name) // Buddy ✅ 继承的属性
重新定义存储属性代码示例:
class Animal {
var name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
var name: String = "New Name" // ❌ 会报错:无法使用存储属性“name”进行覆盖
}
Shadow是什么?
“尝试 shadow”(遮蔽)是面向对象编程中的一个常见术语,意思是:子类中声明了与父类同名的属性或方法,从而“遮蔽”了父类的对应成员。
“Shadowing” = 在子类中用同名成员覆盖/隐藏父类的成员(属性或方法)
class Animal {
var name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
var name: String = "New Name" // ❌ 会报错:‘name’ 属性已在父类中定义
}
Swift 不允许用存储属性 var name 在子类中重复定义父类已有的存储属性;
这样做是 shadow 的一种形式,但 Swift 禁止这种“冲突性 shadow”。
允许的 shadow:方法或计算属性
class Animal {
var name: String {
return "Animal"
}
}
class Dog: Animal {
override var name: String {
return "Dog"
}
}
上面这种是合法的,因为是通过 override 来重写父类的计算属性。这种方式是 Swift 支持的“受控的 shadow”。
需要注意的是,存储属性不允许shadow,因此无法使用override。Override可以重写普通方法、计算数学、下标、required 或 convenience 或 designated 构造器,并且需要符合规则。
存储属性为什么不能重写?
class Animal {
var name: String = "Animal"
}
class Dog: Animal {
override var name: String = "Dog" // ❌ 报错:Cannot override stored property
}
原因是:
父类的存储属性有一块内存;
Swift 不允许子类再重复分配这块内存,会破坏对象内存结构;
只能通过重写计算属性等类型来“覆盖”行为,而不是实际重写内存存储。