属性

实例属性

  • Swift中跟实例相关的属性可以分为2类
    • 存储属性
      • 类似于成员变量的概念
      • 存储在实例的内存中
      • 结构体、类可以定义存储属性
      • 枚举不可以定义存储属性
      • 在创建类或者结构体实例时,必须为所有的存储属性设置一个合适的初始值
        • 可以在初始化器中给存储属性设置初始值
        • 可以分配一个默认的属性值作为属性定义的一部分
    • 计算属性
      • 本质就是方法(函数)
      • 不占用实例的内存
      • 枚举、结构体、类都可以定义计算属性
      • set传入的新值默认叫做newValue,也可以自定义
      • 只读计算属性: 只有get,没有set
      • 只能用var, 不能使用let,计算属性的值可能发生变化的
      • 如果有set方法,必须要有get方法
struct Circle {
    // 存储属性
    var radius: Int
    // 计算属性 本质是方法
    var diameter: Int {
        set {
            radius = newValue / 2
        }
        get {
           radius * 2
        }
    }
}
// 占用内存为8
var c = Circle(radius: 2)
/*
c.diameter = 40
print(c.radius) //20
c.radius = 11
print(c.diameter) // 22
*/
c.radius = 11
/*
  0x100000a4c <+76>:  movq   $0xb, 0x15d1(%rip)        ; _dyld_private + 4
    从汇编可以看出 11 是放在了一个全局变量中, 那只有c这个实例变量
 */

c.diameter = 12
/*
 0x100000a8a <+138>: callq  0x100000b90               ; 闭包.Circle.diameter.setter : Swift.Int at main.swift:16
 可以看出来是调用他的setter方法
 */

枚举的 rawValue本质

  • rawValue本质是 只读的计算属性
  • 是不占用枚举的内存的
  • lazy 属性必须是var, 不能是 let
    • let 必须在实例的初始化方法完成之前就拥有值
  • 如果多条线程同时第一次访问lazy属性 无法保证属性只被初始化一次
enum Person: Int {
    case p, s, d
}
var s = Person.d
var d = s.rawValue
/*
 0x100001711 <+81>:  callq  0x1000017b0               ; 闭包.Person.rawValue.getter : Swift.Int at <compiler-generated>
 调用rawValue本质就是调用 计算属性rawValue的getter方法
 */

延迟存储属性

  • 使用lazy可以定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化
class Car {
    init() {
        print("Car  init !")
    }
    func run() {
        print("Car is running!")
    }
}

class Person {
    lazy var car = Car()
    init() {
        print("Person init!")
    }
    func goOut(){
       car.run()
    }
}

let p = Person()
print("---------------")
p.goOut()
/*
 Person init!
 ---------------
 Car  init !
 Car is running!
 */
class PhotoView {
    lazy var image: Image = {
        let url = ""
        let data = Data(url: url)
        return Image(data: data)
    }()
}
/*注意点
1.当结构体包含一个延迟存储属性时,只有var才能访问它,因为它初始化时需要改变结构体的内存
*/
struct Point {
    var x = 0
    lazy var z = 0
}
let p = Point()
print(p.z)

属性观察器

  • 可以为非lazy的var存储属性设置属性观察器
  • 在初始化器中设置属性是 不会触发 willSet、didSet
struct Point {
    var x: Double {
        willSet {
            print("willSet", newValue)
        }
        didSet {
            print("didSet", oldValue, x)
        }
    }
    init() {
        self.x = 1.0
        print("Point init !")
    }
}

var p = Point()
p.x = 10.5
print(p.x)
/*
 Point init !
 willSet 10.5
 didSet 1.0 10.5
 10.5
 */

属性观察器、计算属性的功能。同样可以应用在全局变量、局部变量身上

inout的本质总结

  • 如果实参有物理内存地址,且没有设置属性观察器

    • 直接将实参的内存地址传入函数(实参进行引用传递)
  • 如果实参是计算属性,或者设置了属性观察器

    • 采取了 Copy In Copy Out的做法
      - 调用该函数时,先复制实参的值,产生副本 get
      - 将副本的内存地址传入函数(副本进行引用传递),在函数内部可以修改副本的值
      - 函数返回后,再将副本的值覆盖实参的值 set
  • 所以 inout 本质就是引用传递

类型属性

只能通过类型去访问的属性

  • 存储类型属性 整个程序运行过程中,就只有一份 ,类似全局变量
  • 计算类型属性

可以用 static 定义类型属性

  • 如果是类 也可以使用 class 关键字
struct Car {
    static var count: Int = 0
    init() {
        Car.count +=  1
    }
}

var a = Car()
var b = Car()
var c = Car()
print(Car.count) //3

注意点

  1. 不同于存储属性,存储类型属性不必设定初始值
    • 因为类型没有像实例那样的init初始化器来初始化存储属性
  2. 默认就是 lazy ,会在第一次使用时初始化
    • 就算被多个线程同时访问,也能保证只会被初始化一次
    • 可以是let
  3. 枚举类型也可以定义存储类型属性、计算类型属性
// 类型属性的应用
class FileManager {
    public static let shared = FileManager()
    private init (){}
}
posted @ 2021-04-19 13:19  YALMiOS  阅读(111)  评论(0)    收藏  举报