Swift笔记-结构体与类

649 阅读5分钟

属性

存储属性

  • 结构体和类可以定义存储属性,枚举不可以
  • 存储在实例内存中
  • 创建结构体和类的实例时,必须为所有存储属性设置一个合适的初始值
延迟存储属性
  • 第一次用到的时候才会初始化
  • 不能保证线程安全
  • 结构体包含一个延迟存储属性时,该结构体的var实例才能访问延迟存储属性,let实例不能访问

计算属性

  • 本质就是方法
  • 不占用实例内存
  • 枚举、结构体和类可以定义计算属性
  • 枚举实例的rawValue为只读计算属性
struct Circle {
    /* 存储属性 */
    var radius: Double
    /* 计算属性 */
    var diameter: Double {
        set {
            radius = newValue / 2
        }
        get {
            radius * 2
        }
    }
    /* 延迟存储属性 */
    lazy var coler = 20
    lazy var borderWidth: Double = {
        return 30
    }()
}

属性观察器

  • 可以给非lazy的var属性添加属性观察器
  • 属性观察器和计算属性的功能可以应用到全局变量和局部变量
    var padding: Double {
        willSet {
            print(newValue)
        }
        didSet {
            print(oldValue)
        }
    }

方法

  • 值类型的实例方法如果想要修改实例属性,需要在该方法前加mutating
struct Person {
    var age = 10
    mutating func passYear() {
        age += 1
    }
}

@discardableResult

  • 可以消除没使用函数返回值的警告
@discardableResult func sum(_ n1: Int, _ n2: Int) -> Int {
    n1 + n2
}
sum(1, 2)

下标(subscript)

  • 可以给任意类型增加下标功能
  • 本质是方法
  • 可以没有set方法,但必须有get方法
  • 只有get方法时,get可以省略
class Point {
    var x = 0.0
    var y = 0.0
    subscript(index: Int) -> Double {
        set {
            if index == 0 {
                x = newValue
            } else if index == 1 {
                y = newValue
            }
        }
        get {
            if index == 0 {
                return x
            } else if index == 1 {
                return y
            }
            return 0
        }
    }
}

var point = Point()
point[0] = 1.1
point[1] = 2.2
print(point.x)      //1.1
print(point.y)      //2.2
print(point[0])     //1.1
print(point[1])     //2.2

  • 结构体作为返回值,想要修改结构体实例属性需要写set方法。而类作为返回值,想要修改类实例属性不需要写set方法
struct Point {
    var x = 0
    var y = 0
}
class PointManager {
    var point = Point()
    subscript(index: Int) -> Point {
        set {
            point = newValue
        }
        get {
            point
        }
    }
}
var manager = PointManager()
manager[0].x = 2 
  • 可以传多个参数
class Grid {
    var data = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]
    ]
    subscript(row: Int, column: Int) -> Int {
        set {
            guard row >= 0 && row < 3 && column >= 0 && column < 3 else {
                return
            }
            data[row][column] = newValue
        }
        get {
            guard row >= 0 && row < 3 && column >= 0 && column < 3 else {
                return 0
            }
            return data[row][column]
        }
    }
}

继承

  • 值类型不支持继承,类可以
  • Swift没有规定必须继承一个类,OC的类几乎都要继承NSObject

重写下标、方法

  • 子类可以重写父类的下标、方法、属性,要加override关键字
  • class修饰的下标、方法可一被子类重写,static修饰的方法、下标不允许被子类重写

重写实例属性

  • 属性可以被重写为计算属性,不能重写成存储属性
  • 只能重写var属性,不能重写let属性
  • 重写的属性名、类型要保持一致
  • 子类重写后的属性权限不能小于父类属性权限

重写类型属性

  • 被class修饰的计算属性,可以被子类重写
  • 被static修饰的属性(存储、计算),不能被子类重写

属性观察器

  • 子类中可以给var存储属性增加属性观察器
  • 子类中可以给父类计算属性增加属性观察器

final

  • 被final修饰的下标、属性、方法禁止被重写
  • 被final修饰的类禁止被继承

多态

  • Swift的多态实现类似于C++的虚表,编译完成时,类的方法存在类型信息中,调用时直接在类型信息中查找

初始化器

  • 类有两种初始化器,指定初始化器和便捷初始化器
    //指定初始化器
    init(parameters) {
        statements
    }
    //便捷初始化器
    convenience init(parameters) {
        statements
    }
  • 指定初始化器为主要初始化器

初始化器调用准则

  • 指定初始化器必须从直系父类调换用初始化器
  • 便捷初始化器必须从相同的类调用其它初始化器
  • 便捷初始化器最终会调用指定初始化器

两段式初始化

  1. 初始化所有存储属性
    • 外层调用初始化器
    • 分配内存给实例,但未初始化
    • 指定初始化器确保当前类定义的存储属性都能初始化
    • 指定初始化器调用父类指定初始化器,不断向上调用,形成初始化链
  2. 设置新的存储属性值
    • 从顶部初始化器往下,链中每个指定初始化器都可以进一步定制实例
    • 初始化器现阶段才能使用self
    • 最终,链中任何便捷初始化器都有机会定制实例及使用self

重写初始化器

  • 把父类的指定初始化器重写为指定或便捷初始化器时,要加override
  • 便捷初始化器不能被子类调用,所以便捷初始化器不会被子类重写

自动继承

  • 如果子类没有自定义任何指定初始化器,它会自动继承父类所有的指定初始化器。如果子类自定义了指定初始化器,则父类的指定初始化器不会继承
  • 如果子类自动继承或者重写了父类所有的指定初始化器,那么父类的便捷初始化器也会自动继承

required

  • 用required修饰指定初始化器,表示其所有子类都必须实现该初始化器
  • 如果子类重写了required初始化器,也必须加上required,不用加override

可失败初始化器

  • 不可定义类型相同的可失败初始化器和非可失败初始化器
  • 可以用init!定义隐式解包的可失败初始化器
  • 可失败初始化器可以调用非可失败初始化器,非可失败初始化器调用可失败初始化器需要进行解包
  • 可以用一个非可失败初始化器重写一个可失败初始化器,但反过来不行

反初始化器(deinit)

  • 类似于C++析构函数,也类似于OC dealloc方法
  • 当类的实例对象被释放时,就会调用deinit方法
  • deinit方法不接收参数,没有(),不能自行调用
  • 先调用子类deinit方法,再调用父类deinit方法