美文网首页
Swift--属性

Swift--属性

作者: Mjs | 来源:发表于2020-12-10 15:03 被阅读0次

    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<Int> // users: %4, %5
      %4 = load %3 : $*Optional<Int>                  // user: %6
      end_access %3 : $*Optional<Int>                 // id: %5
      switch_enum %4 : $Optional<Int>, 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<Int>, #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<Int> // users: %16, %17
      store %13 to %15 : $*Optional<Int>              // id: %16
      end_access %15 : $*Optional<Int>                // 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<Int>, #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<Int> // users: %7, %8
      store %4 to %6 : $*Optional<Int>                // id: %7
      end_access %6 : $*Optional<Int>                 // 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<Int>
    

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

      switch_enum %4 : $Optional<Int>, 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<Int>, #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<Int> // users: %16, %17
      store %13 to %15 : $*Optional<Int>              // id: %16
      end_access %15 : $*Optional<Int>                // id: %17
      br bb3(%11 : $Int)     
    

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

    print(MemoryLayout<Optional<Int>>.size)
    print(MemoryLayout<Optional<Int>>.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<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
    bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
      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--属性

          本文链接:https://www.haomeiwen.com/subject/dralgktx.html