- 定义属性的三种方式
- 直接赋初始值
- 只写类型,在构造器中赋初始值
- 当前是缺省值,以后一定有值。或者当前是缺省值,以后可能还是缺省值。
class Test {
//给属性赋默认值
var a = 0
//给属性类型,默认值在构造器中赋值
var b: Int
//当前是缺省值,
var c: Int?//当前是缺省值,以后可能还是缺省值
var d: Int!//当前是缺省值,以后一定有值
init(b: Int) {
self.b = b
}
}
- 构造器简单使用
struct Color {
var red: Double, green: Double, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red;
self.green = green
self.blue = blue
}
init(white: Double) {
self.red = white
self.green = white
self.blue = white
}
}
var aColor = Color(red: 1.0, green: 0, blue: 0)
var bColor = Color(white: 0.5)
print("aColor: \(aColor)")
print("bColor: \(bColor)")
- 结构体的逐一成员构造器
- 结构体和类都有一个默认的无参数的构造器。
- 结构体比类多了一个默认的逐一成员构造器。
- 结构体的逐一成员构造器调用时候可以省略有默认值的参数。(swift5.1最新版本更新的内容)
struct Test {
var a = 3
var b = 5
}
var a = Test(a: 3, b: 6)
var b = Test(a: 5)
print(a)
print(b)
-
当我们在类或者结构体中写了自定义的构造器后,默认的构造器将消失。
-
类的构造器构造过程
构造过程分为两个阶段阶段一:
- 首先便利构造器调用本类的指定构造器。
- 本类的指定构造器先初始化本类的属性,之后再调用父类的指定构造器。
- 父类的指定构造器再次初始化自身的属性,之后再次调用它的父类的指定构造器。
- 这样就指定构造器就沿着它的继承链向上代理,直到最顶部。这时候完成了整个类的初始化。
阶段二:
- 当指定构造器向上代理到最顶部时候,接下来要向下代理,进行第二阶段
- 接下来沿着指定构造器的继承链向下执行自定义部分(可能没有)
- 最后到达了遍历构造器处,这时候已经完全初始化了本类的所有属性。之后就可以使用这些属性了。
class Test {
var a: Int
init(a: Int) {
print("Test-init-start")
self.a = a
print("Test-init-end")
}
}
class Test1: Test {
var b: Int
init(b: Int, a: Int) {
print("Test1-init-start")
self.b = b
super.init(a: a)
print("Test1-init-end")
}
}
class Test2: Test1 {
var c: Int
init(c:Int, b: Int, a: Int) {
print("Test2-init-start")
self.c = c
super.init(b: b, a: a)
print("Test2-init-end")
}
convenience init(){
//阶段一与阶段二:利用指定构造器完成整个类的属性初始化
print("阶段一:调用指定构造器沿着继承链向上完成属性的初始化 阶段二:在从链顶向下完成自定义部分")
print("convenience init")
self.init(c: 0, b: 0, a: 0)
//最后到达遍历构造器,这时候可以完全访问属性了
let d = (a + 1) * (b + 2) * (c + 3)
print(d)
}
}
//var test = Test2(c: 1, b: 2, a: 3)
var test1 = Test2()
//打印如下:
//阶段一:调用指定构造器沿着继承链向上完成属性的初始化 阶段二:在从链顶向下完成自定义部分
//convenience init
//Test2-init-start
//Test1-init-start
//Test-init-start
//Test-init-end
//Test1-init-end
//Test2-init-end
//6
-
结构体中的构造器
- 结构体中只有指定构造器,且没有继承链。
- 类中有指定构造器,还有遍历构造器,还有继承链。相对更复杂些。
-
构造器的继承和重写
- 如果子类不写任何指定构造器(包括自定义的和父类的),这时候子类将继承所有父类的构造器(包括父类的指定构造器和父类的便利构造器)
- 如果子类把父类的所有指定构造器都重写了,那么这时候子类将会继承父类的所有构造器,主要是父类的遍历构造器。
- 如果子类继承了父类的所有构造器,那么这个时候子类可以选择性的重写父类的遍历构造器,并且前边不用加关键在override。
-
构造器便利构造器的使用
class Food {
var name: String
init(name:String) {
self.name = name
}
convenience init(){
self.init(name: "Unnamed")
}
}
class Material: Food {
var count: Int
init(count: Int, name: String) {
self.count = count
super.init(name: name)
}
convenience override init(name: String) {
self.init(count:1, name:name)
}
}
class ShoppingListItem: Material {
var purchased = false
func description() {
let purchas = purchased ? " ✔" : " ✘"
print("\(count) X \(name) \(purchas)")
}
}
let listItem = [ShoppingListItem(), ShoppingListItem(name: "面包"), ShoppingListItem(count: 5, name: "巧克力")]
listItem[1].purchased = true
listItem[0].description()
listItem[1].description()
listItem[2].description()
- 可失败构造器
- 当在给一个构造器传入非法入参时候,这时候没办法给属性赋值,这时候就可以用可失败构造器。
- 在可失败构造器内部判断非法入参,返回nil就是构造失败了。
struct Animal {
let species: String
init?(species: String){
if species.isEmpty {
return nil
}
self.species = species
}
}
var a = Animal(species: "")
if a == nil{
print("构造失败")
}else{
print("构造成功:" + a!.species)
}
//打印如下
//构造失败
- 枚举类型下的可失败构造器
enum Direction: Character {
case D = "D", X = "X", N = "N", B = "B"
init?(direction: Character){
switch direction {
case "D":
self = .D
case "X":
self = .X
case "N":
self = .N
case "B":
self = .B
default:
return nil
}
}
}
var a = Direction(direction: "D")
print(a!.rawValue)
if let a = Direction(direction: "T") {
print("可选枚举构造成功:\(a.rawValue)")
}else{
print("可选枚举构造失败")
}
- 原始值枚举类型构造器是一个可失败的构造器
enum Direction: Character {
case D = "D", X = "X", N = "N", B = "B"
}
if let a = Direction(rawValue: "T") {
print("可选枚举构造成功:\(a.rawValue)")
}else{
print("可选枚举构造失败")
}
- 构造失败向上代理
class Product {
var name: String
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
}
class CartItem: Product {
var count: Int
init?(name: String, count: Int) {
if count < 1 {
return nil
}
self.count = count
super.init(name: name)
}
}
if let a = CartItem(name: "2", count: 10){
print("构造成功: \(a.name) \(a.count)")
}else{
print("构造失败")
}
- 可失败构造器的继承和重写
- 不可失败构造器可以重写父类的不可失败构造器。
- 可失败构造器可以重写父类的可失败构造器。
- 不可失败构造器可以重写父类的可失败构造器,这时候往往在内部要调用父类的不可失构造器。如果调用那个被重写的可失败构造器,那么就要强制解包,这有可能会发生运行时错误。
- 可失败构造器不能够重写父类的不可失败构造器。
class Product {
var name: String?
init() {}
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
}
class CartItem: Product {
override init() {
super.init()
self.name = "Unnamed"
}
override init(name: String) {
super.init()
if self.name == nil {
self.name = "Unnamed"
}
}
}
var a = CartItem(name: "")
print(a.name!)
- 必要构造器
- 必要构造器就是在init前边加上关键字requird。
- 当子类继承了父类所有构造器后,子类就不必重写父类的必要构造器。
- 当子类没有继承父类的所有构造器的时候,子类必须要重写父类的必要构造器,继续在init方法前边加上requird关键字,不必加override关键字。
class Product {
var name: String
required init(name: String) {
self.name = name
}
}
class CartItem: Product {
var sex: Int = 1
//当该类继承了父类所有构造器的时候,就不必重写父类的必要构造器
//当该类没有继承父类的所有构造器的时候,就要重写父类的必要构造器
init(sex: Int, name: String) {
self.sex = sex
super.init(name: name)
}
required init(name: String) {
self.sex = 0
super.init(name: name)
}
}
var a = CartItem(name: "s")
print(a.name)
- 利用闭包完成属性的初始化
class Squire {
//通过闭包立刻给属性初始化
let colorSquire: [Bool] = {
var tempArr = [Bool]()
var isBlack = false
for _ in 1...8{
for _ in 1...8{
tempArr.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return tempArr
}()
//计算属性
var squireCount: Int {
return colorSquire.count
}
//下标
subscript(row: Int, col: Int) -> Bool{
return colorSquire[row * 8 + col]
}
func squireColor(row: Int, col: Int) -> Bool {
return colorSquire[row * 8 + col]
}
}
var a = Squire()
print(a.squireColor(row: 2, col: 2))
print(a[2,2])
-
计算属性,下标,闭包初始化属性比较
- 计算属性
- 写法是var name:Type{}
- 如果不需要setter方法就可以省略关键字get。get的实现是必须的。
- 计算属性是通过其他的存储属性计算得到的属性。
- 下标
- 写法是subscript(参数) -> type{}
- 如果不需要setter方法就可以省略关键字get。get的实现是必须的。
- 闭包初始化属性
- 写法是var name:type = {}()
- 如果不需要setter方法就可以省略关键字get。get的实现是必须的。
- 比较
- 计算属性目的是其他存储属性计算得到。{}不是等于号,不用立刻执行。
- 下标的目的是通过下标来访问成员。写法是必须要有关键字写法是subscript,subscript(参数) -> type{}。
- 闭包初始化属性,目的是立刻通过一些逻辑来完成属性的初始化,{}要有等于号,是创建实例对象时候立刻执行的()。
- 计算属性
-
计算属性,下标,闭包初始化属性使用场景
- 该属性要通过一些存储属性的计算得到,这时候要用计算属性。
- 该对象要通过下标来访问内部数据,要用下标。
- 如果创建对象时候就要进行一些逻辑计算等来创建一个属性,就用闭包来初始化这个属性。
-
总结
- 结构体中只有指定构造器,没有遍历构造器。使用时候就是在init方法内部初始化本类属性即可。
- 类中既有指定构造器还有遍历构造器
- 指定构造器是先初始化本类属性,之后调用父类构造器。最后修改父类属性。
- 遍历构造器内部一定要先调用本类的指定构造器,完成所有属性初始化后再去用这些属性。
- 类中构造器的继承
- 子类中如果一个指定构造器都没有写,那么子类会继承父类的全部构造器(包括父类的指定构造器和遍历构造器)
- 子类如果重写了父类的所有指定构造器(可以把父类的指定构造器重写成遍历构造器),那么子类会自动继承父类的所有构造器(包括父类的指定构造器和遍历构造器)
- 当子类自动继承了父类的所有构造器后,子类可以选择性的重写父类的遍历构造器,并且不用使用override关键字。
- 结构体中默认有无参数的构造器和成员逐一构造器,并且swift5.1支持调用时候可以缺省有默认值的参数。
类中默认只有无参数的构造器。并且一旦写了自定义的构造器,那么结构体和类中的默认构造器将会失效。