Swift--属性

Swift属性

存储属性(要么是常量(let 修饰)存储属性,要么是变量(var 修饰)存储属性)
计算属性(顾名思义计算属性是不占⽤存储空间的,本质get/set⽅法)
class Square{
    var width:Double = 20
    var area:Double{
        get{
            width * width
        }
        set{
            width = sqrt(newValue)
        }
    }
}
print(class_getInstanceSize(Square.self))
······················
24

swiftc -emit-sil main.swift >> ./main.sil && open main.sil编译一下查看sil文件


class Square {
  @_hasStorage @_hasInitialValue var width: Double { get set }
  var area: Double { get set }
  @objc deinit
  init()
}

OC中的方法存放在Method_list
Swift方法存在Metadata中

属性观察者: (willSet,didSet)

1.定义的存储属性

class Teacher{
    //属性观察者
    var name: String = "OC"{
        //新值存储之前调用
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存储之后会被调用
        didSet{
            print("didSet oldValue\(oldValue)")
        }
    }
    
}
var t = Teacher()
t.name = "Swift"
···············
willSet newValueSwift
didSet oldValueOC

我们查看sil文件

// Teacher.name.setter
sil hidden @$s4main7TeacherC4nameSSvs : $@convention(method) (@owned String, @guaranteed Teacher) -> () {
// %0                                             // users: %22, %16, %12, %11, %2
// %1                                             // users: %20, %13, %11, %4, %3
bb0(%0 : $String, %1 : $Teacher):
  debug_value %0 : $String, let, name "value", argno 1 // id: %2
  debug_value %1 : $Teacher, let, name "self", argno 2 // id: %3
  %4 = ref_element_addr %1 : $Teacher, #Teacher.name // user: %5
  %5 = begin_access [read] [dynamic] %4 : $*String // users: %6, %8
  %6 = load %5 : $*String                         // users: %21, %20, %9, %7
  retain_value %6 : $String                       // id: %7
  end_access %5 : $*String                        // id: %8
  debug_value %6 : $String, let, name "tmp"       // id: %9
  // function_ref Teacher.name.willset
  %10 = function_ref @$s4main7TeacherC4nameSSvw : $@convention(method) (@guaranteed String, @guaranteed Teacher) -> () // user: %11
  %11 = apply %10(%0, %1) : $@convention(method) (@guaranteed String, @guaranteed Teacher) -> ()
  retain_value %0 : $String                       // id: %12
  %13 = ref_element_addr %1 : $Teacher, #Teacher.name // user: %14
  %14 = begin_access [modify] [dynamic] %13 : $*String // users: %16, %15, %18
  %15 = load %14 : $*String                       // user: %17
  store %0 to %14 : $*String                      // id: %16
  release_value %15 : $String                     // id: %17
  end_access %14 : $*String                       // id: %18
  // function_ref Teacher.name.didset
  %19 = function_ref @$s4main7TeacherC4nameSSvW : $@convention(method) (@guaranteed String, @guaranteed Teacher) -> () // user: %20
  %20 = apply %19(%6, %1) : $@convention(method) (@guaranteed String, @guaranteed Teacher) -> ()
  release_value %6 : $String                      // id: %21
  release_value %0 : $String                      // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s4main7TeacherC4nameSSvs'

这是name的setter方法
willset是将%0也就是newValue传了进去,didset,6%溯源也就是Teacher.name。
注意的是

class Teacher{
    //属性观察者
    var name: String = "OC"{
        //新值存储之前调用
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存储之后会被调用
        didSet{
            print("didSet oldValue\(oldValue)")
        }
    }
  //初始化我们的变量
    init() {
      //不会触发属性观察者
        self.name = "Swift"
    }
    
}
var t = Teacher()

什么都不会打印,因为init只是初始化我们的变量,如果触发了,有可能发生方位未定义的变量,导致内存报错。
2.继承的存储属性

class Teacher{
    var age:Int=18
    //属性观察者
    var name: String = "OC"{
        //新值存储之前调用
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存储之后会被调用
        didSet{
            print("didSet oldValue\(oldValue)")
        }
    }
    init() {
        self.name = "Swift"
    }
    
}
class ITTeacher: Teacher {
    override var age: Int{
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存储之后会被调用
        didSet{
            print("didSet oldValue\(oldValue)")
        }
    }
}

继承的存储属性也可以添加属性观察者
3.继承的计算属性

class Teacher{
    var age:Int=18
    //属性观察者
    var age2:Int{
        get{
            age
        }
        set{
            age = newValue
        }
    }
    
}
class ITTeacher: Teacher {
    override var age2: Int{
        willSet{
            
        }
        didSet{
            
        }
    }
}

但是在自己本身的实现中不能添加属性观察者

class Teacher{
    var age:Int=18{
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存储之后会被调用
        didSet{
            print("didSet oldValue\(oldValue)")
        }
    }
    
}
class ITTeacher: Teacher {
    override var age: Int{
        willSet{
            print("override willSet newValue\(newValue)")
        }
        //新值存储之后会被调用
        didSet{
            print("override didSet oldValue\(oldValue)")
        }
    }
}

var t = ITTeacher()
t.age = 20
·······················
override willSet newValue20
willSet newValue20
didSet oldValue18
override didSet oldValue18

我们可以看到子类调用时属性观察者调用顺序。

override init() {
        super.init()
        self.age = 100
    }

如果在子类中init修改了属性也会触发

延迟存储属性
class Teacher{
    lazy var age:Int=18
    
}

var t = Teacher()
t.age = 30
  • 延迟存储属性必须有⼀个默认的初始值
  • 延迟存储在第⼀次访问的时候才被赋值
    查看sil文件
class Teacher {
  lazy var age: Int { get set }
  @_hasStorage @_hasInitialValue final var $__lazy_storage_$_age: Int? { get set }
  @objc deinit
  init()
}

// Teacher.age.getter
sil hidden @$s4main7TeacherC3ageSivg : $@convention(method) (@guaranteed Teacher) -> Int {
// %0                                             // users: %14, %2, %1
bb0(%0 : $Teacher):
  debug_value %0 : $Teacher, let, name "self", argno 1 // id: %1
  %2 = ref_element_addr %0 : $Teacher, #Teacher.$__lazy_storage_$_age // user: %3
  %3 = begin_access [read] [dynamic] %2 : $*Optional // users: %4, %5
  %4 = load %3 : $*Optional                  // user: %6
  end_access %3 : $*Optional                 // id: %5
  switch_enum %4 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 // id: %6

// %7                                             // users: %9, %8
bb1(%7 : $Int):                                   // Preds: bb0
  debug_value %7 : $Int, let, name "tmp1"         // id: %8
  br bb3(%7 : $Int)                               // id: %9

bb2:                                              // Preds: bb0
  %10 = integer_literal $Builtin.Int64, 18        // user: %11
  %11 = struct $Int (%10 : $Builtin.Int64)        // users: %18, %13, %12
  debug_value %11 : $Int, let, name "tmp2"        // id: %12
  %13 = enum $Optional, #Optional.some!enumelt.1, %11 : $Int // user: %16
  %14 = ref_element_addr %0 : $Teacher, #Teacher.$__lazy_storage_$_age // user: %15
  %15 = begin_access [modify] [dynamic] %14 : $*Optional // users: %16, %17
  store %13 to %15 : $*Optional              // id: %16
  end_access %15 : $*Optional                // id: %17
  br bb3(%11 : $Int)                              // id: %18

// %19                                            // user: %20
bb3(%19 : $Int):                                  // Preds: bb2 bb1
  return %19 : $Int                               // id: %20
} // end sil function '$s4main7TeacherC3ageSivg'


// Teacher.age.setter
sil hidden @$s4main7TeacherC3ageSivs : $@convention(method) (Int, @guaranteed Teacher) -> () {
// %0                                             // users: %4, %2
// %1                                             // users: %5, %3
bb0(%0 : $Int, %1 : $Teacher):
  debug_value %0 : $Int, let, name "value", argno 1 // id: %2
  debug_value %1 : $Teacher, let, name "self", argno 2 // id: %3
  %4 = enum $Optional, #Optional.some!enumelt.1, %0 : $Int // user: %7
  %5 = ref_element_addr %1 : $Teacher, #Teacher.$__lazy_storage_$_age // user: %6
  %6 = begin_access [modify] [dynamic] %5 : $*Optional // users: %7, %8
  store %4 to %6 : $*Optional                // id: %7
  end_access %6 : $*Optional                 // id: %8
  %9 = tuple ()                                   // user: %10
  return %9 : $()                                 // id: %10
} // end sil function '$s4main7TeacherC3ageSivs'

@_hasStorage @_hasInitialValue final var $__lazy_storage_$_age: Int? { get set }是个可选类型。

store %4 to %6 : $*Optional

setter方法中将一个确定的值赋值了过去

  switch_enum %4 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 // id: %6

如果有值就走bb1,没有值就走bb2


bb2:                                              // Preds: bb0
  %10 = integer_literal $Builtin.Int64, 18        // user: %11
  %11 = struct $Int (%10 : $Builtin.Int64)        // users: %18, %13, %12
  debug_value %11 : $Int, let, name "tmp2"        // id: %12
  %13 = enum $Optional, #Optional.some!enumelt.1, %11 : $Int // user: %16
  %14 = ref_element_addr %0 : $Teacher, #Teacher.$__lazy_storage_$_age // user: %15
  %15 = begin_access [modify] [dynamic] %14 : $*Optional // users: %16, %17
  store %13 to %15 : $*Optional              // id: %16
  end_access %15 : $*Optional                // id: %17
  br bb3(%11 : $Int)     

将20赋值给他
所以延迟存储属性就是通过enum的分支Optional来确定的。enum也是一个值类型,我们可以通过MemoryLayout>

print(MemoryLayout>.size)
print(MemoryLayout>.stride)
···············
9
16

多余的1字节为case,根据字节对齐规则。
所以Teacher类占用的内存为32。

  • 延迟存储属性并不能保证线程安全。

类型属性

class Teacher{
    static var age:Int=18
}

var age = Teacher.age

swiftc -emit-sil main.swift | xcrun swift-demangle >> ./main.sil && open main.sil打开sil文件


class Teacher {
  @_hasStorage @_hasInitialValue static var age: Int { get set }
  @objc deinit
  init()
}

@_hasStorage @_hasInitialValue var age: Int { get set }

// globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_token0
sil_global private @globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_token0 : $Builtin.Word

// static Teacher.age
sil_global hidden @static main.Teacher.age : Swift.Int : $Int

// age
sil_global hidden @main.age : Swift.Int : $Int

Teacher.age变成了一个全局变量。


// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>):
  alloc_global @main.age : Swift.Int                   // id: %2
  %3 = global_addr @main.age : Swift.Int : $*Int       // user: %9
  %4 = metatype $@thick Teacher.Type
  // function_ref Teacher.age.unsafeMutableAddressor
  %5 = function_ref @main.Teacher.age.unsafeMutableAddressor : Swift.Int : $@convention(thin) () -> Builtin.RawPointer // user: %6
  %6 = apply %5() : $@convention(thin) () -> Builtin.RawPointer // user: %7
  %7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*Int // user: %8
  %8 = begin_access [read] [dynamic] %7 : $*Int   // users: %10, %9
  copy_addr %8 to [initialization] %3 : $*Int     // id: %9

在这里%3就是我们的全局变量age,将8%赋值给他,8%最庸调用的是5%方法@main.Teacher.age.unsafeMutableAddressor


// Teacher.age.unsafeMutableAddressor
sil hidden [global_init] @main.Teacher.age.unsafeMutableAddressor : Swift.Int : $@convention(thin) () -> Builtin.RawPointer {
bb0:
  %0 = global_addr @globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_token0 : $*Builtin.Word // user: %1
  %1 = address_to_pointer %0 : $*Builtin.Word to $Builtin.RawPointer // user: %3
  // function_ref globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0
  %2 = function_ref @globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0 : $@convention(c) () -> () // user: %3
  %3 = builtin "once"(%1 : $Builtin.RawPointer, %2 : $@convention(c) () -> ()) : $()
  %4 = global_addr @static main.Teacher.age : Swift.Int : $*Int // user: %5
  %5 = address_to_pointer %4 : $*Int to $Builtin.RawPointer // user: %6
  return %5 : $Builtin.RawPointer                 // id: %6
} // end sil function 'main.Teacher.age.unsafeMutableAddressor : Swift.Int'

在这里又调用了@globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0方法,这里还有一点要注意的是builtin "once"这个方法

sil private @globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0 : $@convention(c) () -> () {
bb0:
  alloc_global @static main.Teacher.age : Swift.Int         // id: %0
  %1 = global_addr @static main.Teacher.age : Swift.Int : $*Int // user: %4
  %2 = integer_literal $Builtin.Int64, 18         // user: %3
  %3 = struct $Int (%2 : $Builtin.Int64)          // user: %4
  store %3 to %1 : $*Int                          // id: %4
  %5 = tuple ()                                   // user: %6
  return %5 : $()                                 // id: %6
} // end sil function 'globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0'

在这里就是将20赋值给了全局变量main.Teacher.age

builtin "once"其实调用的就是swift_once

void swift::swift_once(swift_once_t *predicate, void (*fn)(void *),
                       void *context) {
#if defined(__APPLE__)
  dispatch_once_f(predicate, context, fn);
#elif defined(__CYGWIN__)
  _swift_once_f(predicate, context, fn);
#else
  std::call_once(*predicate, [fn, context]() { fn(context); });
#endif
}

这里调用的是GCD的dispatch_once_f,我们声明static可以保证只初始化一次。

单例

所以我们根据static关键字就可以实现单例

class LGTeacher{ 
 static let sharedInstance: LGTeacher = LGTeacher() 
 private init(){} 
 }

你可能感兴趣的:(Swift--属性)