Swift--屬性

Swift屬性

存儲屬性(要么是常量(let 修飾)存儲屬性,要么是變量(var 修飾)存儲屬性)
計算屬性(顧名思義計算屬性是不占?存儲空間的,本質(zhì)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"{
        //新值存儲之前調(diào)用
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存儲之后會被調(diào)用
        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傳了進(jìn)去,didset,6%溯源也就是Teacher.name。
注意的是

class Teacher{
    //屬性觀察者
    var name: String = "OC"{
        //新值存儲之前調(diào)用
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存儲之后會被調(diào)用
        didSet{
            print("didSet oldValue\(oldValue)")
        }
    }
  //初始化我們的變量
    init() {
      //不會觸發(fā)屬性觀察者
        self.name = "Swift"
    }
    
}
var t = Teacher()

什么都不會打印,因為init只是初始化我們的變量,如果觸發(fā)了,有可能發(fā)生方位未定義的變量,導(dǎo)致內(nèi)存報錯。
2.繼承的存儲屬性

class Teacher{
    var age:Int=18
    //屬性觀察者
    var name: String = "OC"{
        //新值存儲之前調(diào)用
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存儲之后會被調(diào)用
        didSet{
            print("didSet oldValue\(oldValue)")
        }
    }
    init() {
        self.name = "Swift"
    }
    
}
class ITTeacher: Teacher {
    override var age: Int{
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存儲之后會被調(diào)用
        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{
            
        }
    }
}

但是在自己本身的實現(xiàn)中不能添加屬性觀察者

class Teacher{
    var age:Int=18{
        willSet{
            print("willSet newValue\(newValue)")
        }
        //新值存儲之后會被調(diào)用
        didSet{
            print("didSet oldValue\(oldValue)")
        }
    }
    
}
class ITTeacher: Teacher {
    override var age: Int{
        willSet{
            print("override willSet newValue\(newValue)")
        }
        //新值存儲之后會被調(diào)用
        didSet{
            print("override didSet oldValue\(oldValue)")
        }
    }
}

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

我們可以看到子類調(diào)用時屬性觀察者調(diào)用順序。

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

如果在子類中init修改了屬性也會觸發(fā)

延遲存儲屬性
class Teacher{
    lazy var age:Int=18
    
}

var t = Teacher()
t.age = 30
  • 延遲存儲屬性必須有?個默認(rèn)的初始值
  • 延遲存儲在第?次訪問的時候才被賦值
    查看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字節(jié)為case,根據(jù)字節(jié)對齊規(guī)則。
所以Teacher類占用的內(nèi)存為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%最庸調(diào)用的是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'

在這里又調(diào)用了@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"其實調(diào)用的就是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
}

這里調(diào)用的是GCD的dispatch_once_f,我們聲明static可以保證只初始化一次。

單例

所以我們根據(jù)static關(guān)鍵字就可以實現(xiàn)單例

class LGTeacher{ 
 static let sharedInstance: LGTeacher = LGTeacher() 
 private init(){} 
 }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容