1. 构造器的基本概念以及存储属性直接初始化:
1) Swift的构造器统一叫init,之所以不属于方法的范畴是因为其没有返回值,但是它也有和方法相似的功能,顾名思义和C++等的构造函数的意义是一样的,都是用来完成初始化任务,而通过类名创建实例的背后其实是调用了init构造器;
2) Swift强行要求构造器的参数必须有外部参数名,这是因为Swift构造器可以重载,并且Swift的重载也包括返回值类型,因此有可能构造器会重载很多,为了调用时区分不同的构造器要求都必须加上外部参数名(调用时如果不适用外部参数名会直接报错的),同时Swift允许外部参数名重载,即使两个函数的函数类型完全一样,但是只要两个函数的外部参数名不同也能形成重载(这点非常之狠!这就可以各种随意重载了!超级牛逼的!);
3) Swift要求构造器的每个参数都具有外部参数名,如果不用#修饰则内外参数名相同,当然也可以用一般的方式自己定义自己的外部参数名;
4) 直接初始化就是指在定义存储属性的时候就对其初始化,这里需要强调一下直接初始化和调用构造器之间的先后顺序:Swift会先调用构造器对相关的属性进行初始化,接着再检查直接初始化语句,如果发现还有构造器没有初始化过的属性就是用直接初始化语句对那些属性进行初始化,而那些已经用构造器初始化过的属性就不再初始化了!
5) 构造器可以对let定义的常量属性初始化,并且如果常量属性同时在直接初始化语句中初始化过也在构造器中初始化过也不会发生冲突而导致编译出错!为什么呢?还记得4)中讲过的规则了吗?因为如果在构造器中对其初始化过了,直接初始化语句就会被忽略掉,因此常量属性只会被初始化一次而不是两次,因此不会报错!(记住!常量只能初始化一次!)
6) 初始化示例:
class Test
{
var a: Int = 9 // 构造器中没初始化过就会执行此句
let b: Int = 10 // 构造器初始化过了这句就会被忽略
init(b: Int) {
self.b = b // 先在构造器中初始化
}
}
var t = Test(b: 27)
println("a = \(t.a), b = \(t.b)") // 9, 27
class Test2
{
let a: Int // 不必定义时就直接初始化
init() {
a = 10 // 只初始化一次
}
}
println(Test2().a) // 10
!建议所有属性只在一个地方初始化,不要即直接初始化也有构造器初始化,程序调理一定要清楚!
2. 默认构造器:
1) 默认构造器就是指用户不定义任何构造器时Swift会为我们隐式提供的构造器;
2) 对于类,默认构造器只有一个无参的空init,而对于结构体,还多了一个这样的构造器,请看如下:
还有就是可选类型的初始化一定要注意了,类和结构体有一定区别:
class A {
// 只有一个空的无参构造器,因此无法通过用户传参构造
// 所以必须直接初始化,否则永远也不能初始化而导致编译错误!
var a: Int = 1
// init() {} // 默认提供一个这个
}
var aa = A() // OK!
// var aa2 = A(a: 15) // Bad! 类不提供带参的逐一初始化构造器
struct B {
var a: Int
var b: Int
// init() {} // 默认提供
// init(a: Int, b: Int) { // 还默认提供一个这个,外部参数名和定义时一样
// self.a = a
// self.b = b
// }
}
// var bb = B() // Bad!由于没有直接初始化语句,所以这种方式将导致成员无法初始化而报错!
// 但是如果有直接初始化语句就对了!
var bb = B(a: 23, b: 32) // OK!
class C_Class {
var a: Int?
}
var cc = C_Class() // 类可以不初始化可选类型
struct C_Struct {
var a: Int?
}
// var cc2 = C_Struct() // Bad! 但是结构体必须初始化可选类型
var cc2 = C_Struct(a: nil) // OK!
var cc3 = C_Struct(a: 13) // OK!
3. 构造器重载:
函数类型、外部参数名不同都能重载,就尽情地重载吧!
class A
{
var a: Int = 1
var b: Int = 2
var c: Int = 3
init() {
a = 15
c = 20
}
init(s: String, a: Int, b: Int) {
println(s)
self.a = a
self.c = b
}
init(a: Int, c: Int) {
self.a = a
self.c = c
}
init(aa a: Int, cc c: Int) { // 外部参数名不同也能重载
self.a = a
self.c = c
}
}
4. 构造器代理:
1) 和Java和C++一样,为了不重复写代码会在一个构造器中调用另一个构造器;
2) Swift也支持这个特性,这就是代理构造器,即在一个构造器中调用另一个构造器的代码,但不过调用构造器的语句必须是第一句并且仅且能调用最多一句!否则会报错!并且调用的时候一定要加self.;
3) 结构体和类的代理构造器略有不同,结构体构造器代理非常随意,直接写就行了,而类对于调用构造器的构造器必须用convenience修饰,因此对于类的代理构造器也称为便利构造器,这里就不掩饰结构体的代理构造器了,因为只需要将class改为struct并把convenience去掉就行了:
class A {
var a: Int = 2
var b: Int = 5
var c: Int = 9
init(a: Int, c: Int) {
self.a = a
self.c = c
}
convenience init(s: String) {
self.init(a: 20, c: 30) // 必须是第一句,并且最多只能一句,否则就会报错
b = 13
}
}
4) 由于类具有继承特性(结构体没有),和C++一样,对于子类需要先调用父类构造器来初始化父类的部分,然后在执行自己的初始化语句,因此对于这类的代理就称为纵向代理(也称为向上代理),这样的构造器就是指定构造器,而对于上述的代理构造仅仅就是一个类内部之间的构造器相互代理,因此称为横向代理,构造器称为便利构造器;
5. 析构器:
1) 同样也不属于方法,由于无参无返回值(和C++的情况一样),因此不能重载,因此有且仅有一个,名字限制为deinit,没有参数列表,因此不需要参数的括号;
2) 也是用于清理工作,虽然Swift的ARC内存管理机制(引用计数)和Java效果一样,可以使程序员完全不用关心对象没有及时析构的问题,但是Swift仍然提供了析构器,仍然用于完成对象的清理工作,虽然实例资源不需要程序员管,但是如关闭文件等操作还是需要程序员来执行的,因此关闭文件等操作可以写在析构器代码中,因此Swift还是非常贴心周到的;
3) 如果用户不写析构器则Swift会自动定义一个空的默认析构器;
4) 析构器调用时机和C++的析构函数一样,都是在对象被销毁之前调用;
5) 一个简单的示例:
class Test {
deinit {
println("deinit!")
}
}
var t: Test? = Test()
t = nil // deinit!
!!!特别强调!
如果用户自定义了任何一个构造器,则默认构造器就不存在了,请看下例:
struct A {
var a: Int
init() {} // Bad! 编译错误
}
由于用户自定义了一个无参空构造器,因此Swift默认定义的隐式构造器都不存在了,就连注意赋值的默认构造器也不存在了,因此会导致a无法初始化而报错!