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<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(){} 
 }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容