- 构造方法作用:对实例对象的内容进行初始化
Swift要求类或者结构体中的存储属性(非lazy属性)在对象构造完毕后要有初始化值
语法:
init(参数列表){ 初始化代码 }
注意
- 构造方法的作用仅仅是用于初始化属性, 而不是分配内存, 分配内存是系统帮我们做的
- 构造方法是隐式调用的, 通过类名称() 形成创建一个对象就会隐式调用 init() 构造方法
- 如果所有的存储属性都有默认值, 可以不提供构造方法, 系统会提供一个隐式的构造方法;
- 如果存储属性可以提供默认值, 那么提倡大家使用设置默认值的方法, 这样可以简化代码,不用写构造方法
class Person {
var name: String = "hjq"
// var age: Int = 20
var age:Int
func description() -> String {
return "name = \(name) age = \(age)"
}
init() {
print("init")
age = 30
}
}
// 1.分配内存; 2.初始化name和age; 3.构造方法是隐式调用的
var p = Person()
print(p.description())
print(p.age)
- 带参数的构造方法
class Person2 {
var name:String
var age:Int
func description() -> String {
return "name = \(name) age = \(age)"
}
init(age:Int, name:String)
{
self.name = name
self.age = age
}
func setName(name:String, age:Int)
{
self.name = name
self.age = age
}
}
var p2 = Person2(age: 25, name: "hjq")
p2.setName(name: "hjq", age: 30)
print(p2.description())
以上存储属性都没有提供默认值,必须提供构造方法
,而构造方法是带参数的,所以创建对象时必须带参数来初始化存储属性,而不能用Person2()
- 结构体和构造方法
struct Rect {
var width:Double
var height:Double
/*
// 系统默认会提供一个类似的方法
init(width:Double, height:Double)
{
self.width = width
self.height = height
}
*/
}
var r = Rect(width: 20, height: 20)
以上存储属性都没有默认值,故必须用默认的逐一构造器初始化结构体,不存在Rect()的方式初始化结构体。
如果在结构体中自定义了构造方法, 那么系统不会生成默认的逐一构造器。
如果给存储属性提供了默认值, 系统还是会提供默认的逐一构造器
- 构造器代理: 构造方法之间的相互调用,即在构造方法中可以调用其他构造方法来完成实例的构造。好处减少不必要的代码
struct Rect2 {
var width:Double, height:Double
init(width:Double, height:Double)
{
self.width = width
self.height = height
}
init()
{
// self.width = 0.0
// self.height = 0.0
//构造器代理
self.init(width: 0, height: 0)
}
func show()
{
print("width = \(width) height = \(height)")
}
}
var r2 = Rect2()
r2.show()
var r3 = Rect2(width: 100, height: 100)
r3.show()
- 指定构造器与便利构造器
- 结构体是值类型,类是引用类型。引用类型的init()方法前必须加convenience关键字,这样的构造器被称为便利构造器。而带参数的init(name:String, age:Int)方法被称为指定构造器。
- 便利构造器中必须出现self.init。
- 指定构造器和便利构造器不能是相同参数
class Person {
var name:String
var age:Int
//指定构造方法都是以init开头
init(name:String, age:Int)
{
self.name = name
self.age = age
}
//1.如果是值类型没问题, 称之为构造器代理;
//2.但是如果是引用类型会报错, 需要在前面加上 convenience关键字;
//3.被convenience关键字修饰的构造方法称之为便利构造器, 通过调用其他构造方法来初始化;
//4.反而言之, 便利构造器中一定是调用其他构造方法初始化的, 一定要出现self.init
convenience init()
{
self.init(name: "hjq", age: 26)
}
//类可以拥有多个构造方法
init(name:String)
{
self.name = name
self.age = 0
//不能再指定构造方法中调用便利构造器方法
//换言之,指定构造方法中不能出现self.init
// self.init()
}
convenience init(age:Int)
{
//可以在便利构造器中调用指定构造器
// self.init(name: "hjq", age: 24)
self.init()
}
// 便利构造器不能和指定构造器同名
// convenience init(name:String)
// {
// }
}
- 派生类的构造方法
class Man {
var name:String
//指定构造器
init(name:String)
{
self.name = name
}
convenience init(){
self.init(name: "hjq")
}
}
class SuperMan: Man {
var age:Int
// 注意:
// 1.默认情况下基类的构造方法不会被继承
// 2.基类的存储属性只能通过基类的构造方法初始化
// 3.初始化存储属性时必须先初始化当前类再初始化父类
// 4.不能通过便利构造器初始化父类, 只能通过调用指定构造器初始化父类
//指定构造器
init(age:Int) {
self.age = age
super.init(name: "han") //必须调用父类的指定构造器
// super.init() //不能调用父类的便利构造器
}
}
- 构造器间的调用规则
- 1.子类指定构造器必须调用其直接父类的"指定构造器"
- 2.子类便利构造器必须调用当前类中的其他构造器(指定或者便利),且最终必须调用一个指定构造器结束(无论指定还是便利, 最终肯定调用一个指定构造器)
- 3.子类指定构造器必须出现super.init,便利构造器不要super.init
class Man2 {
var name:String
//指定构造器
init(name:String) {
self.name = name
}
convenience init(){
self.init(name: "HaRi")
}
}
class SuperMan2: Man2 {
var age:Int
//指定构造器
init(age:Int) {
self.age = age
super.init(name: "xiaohange")
}
convenience init()
{
self.init(age: 25)
}
convenience init(name: String, age: Int) {
//调用当前类构造器一定能够初始化所有属性
// self.init(age: 30)
//便利构造器中只能通过self.init来初始化, 不能使用 super.init
//因为调用父类构造器不一定完全初始化所有属性(子类持有)
// super.init(name: "han")
self.init()
}
}
- 两段式构造。构造过程可以划分为两个阶段:
1.确保当前类和父类所有存储属性都被初始化
2.做一些其他初始化操作
好处:可以防止属性在被初始化前访问
class Man3 {
var name:String
//指定构造方法
init(name:String) {
self.name = name
}
//便利构造方法
convenience init(){
self.init(name: "hello world")
}
}
class SuperMan3: Man3 {
var age:Int
init(age:Int) {
print("SuperMan第一阶段开始")
//对子类引入的属性初始化
self.age = age
//代码会报错, 因为调用self.name之前还没有对父类的name进行初始化
//即便在这个地方修改, 也会被后面的初始化语句覆盖
// if age > 30 {
// self.name = "hjq"
// }
//对父类引入的属性进行初始化
super.init(name: "han")
print("SuperMan第二阶段开始")
if age > 30 {
self.name = "hello xiaohange"
}
}
}
- 必须构造器。父类构造器加了required关键字,则子类必须重写该构造器,且带上required关键字
class Person7 {
var name:String
required init(name:String){
self.name = name
}
}
class SuperMan7: Person7 {
var age:Int
init() {
self.age = 24
super.init(name: "hjq")
}
required init(name: String) {
self.age = 24
super.init(name: name)
}
}
var sm7 = SuperMan7(name: "hjq")