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(){}
}