实例对象的结构
class Teacher {
var age: Int = 18;
var name: String = "erge";
}
var t = Teacher();
初始化的时候汇编调用如下:
SwiftDemo`Teacher.__allocating_init():
0x100003c90 <+0>: pushq %rbp
0x100003c91 <+1>: movq %rsp, %rbp
0x100003c94 <+4>: pushq %r13
0x100003c96 <+6>: pushq %rax
0x100003c97 <+7>: movq %r13, %rdi
0x100003c9a <+10>: movl $0x28, %esi
0x100003c9f <+15>: movl $0x7, %edx
-> 0x100003ca4 <+20>: callq 0x100003d7e ; symbol stub for: swift_allocObject
0x100003ca9 <+25>: movq %rax, %r13
0x100003cac <+28>: callq 0x100003ce0 ; SwiftDemo.Teacher.init() -> SwiftDemo.Teacher at main.swift:10
0x100003cb1 <+33>: addq $0x8, %rsp
0x100003cb5 <+37>: popq %r13
0x100003cb7 <+39>: popq %rbp
0x100003cb8 <+40>: retq
这里比较重要的是 swift_allocObject 的过程,下面是 Swift-source 的源码:
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
assert(isAlignmentMask(requiredAlignmentMask));
// 创建内存大小,字节对齐
auto object = reinterpret_cast<HeapObject *>(
swift_slowAlloc(requiredSize, requiredAlignmentMask));
// NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
// check on the placement new allocator which we have observed on Windows,
// Linux, and macOS.
new (object) HeapObject(metadata);
// If leak tracking is enabled, start tracking this object.
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;
}
HeapObject 的结构如下:
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *metadata; // 元类型 8字节
// InlineRefCounts refCounts
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS; // 引用计数 8 字节
#ifndef __swift__
HeapObject() = default;
// Initialize a HeapObject header as appropriate for a newly-allocated object.
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Initialized)
{ }
// Initialize a HeapObject header for an immortal object
constexpr HeapObject(HeapMetadata const *newMetadata,
InlineRefCounts::Immortal_t immortal)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Immortal)
{ }
#ifndef NDEBUG
void dump() const LLVM_ATTRIBUTE_USED;
#endif
#endif // __swift__
};
我们可以得出一些简单的结论:
- Swift 内存分配的过程:__allocating_init —> swift_allocObject —> __swift_allocObject__ —> swift_slowAlloc —> malloc;
- Swift 对象的内存结构 HeapObject 有两属性:metadata(8字节)、refCounts(8字节),默认占用 16字节大小;
- init 主要是初始化变量;
查看类的大小:
print(class_getInstanceSize(Teacher.self))
// 40
print(MemoryLayout<String>.stride);
// 16
所以 Teacher 类的大小如下计算:
class Teacher {
// 16
var age: Int = 18; // 8
var name: String = "erge"; // 16
}
var t = Teacher();
类的结构
template <typename Runtime>
struct TargetHeapMetadata : TargetMetadata<Runtime> {
using HeaderType = TargetHeapMetadataHeader<Runtime>;
TargetHeapMetadata() = default;
// 如果是单纯的 Swift 类就走这个
constexpr TargetHeapMetadata(MetadataKind kind)
: TargetMetadata<Runtime>(kind) {}
#if SWIFT_OBJC_INTEROP
// 如果是继承 NSObject @objc 走这个
constexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa)
: TargetMetadata<Runtime>(isa) {}
#endif
};
MetadataKind 的类型如下:
继承关系
TargetClassMetadata(所有属性)
---> TargetAnyClassMetadata(kind, superClass, cacheData)
---> TargetHeapMetadata
---> TargetMetadata(kind)
metaData的数据结构如下:
struct swift_class_t: NSObject {
void *kind; //isa, kind(unsigned long)
void *superClass;
void *cacheData
void *data
uint32_t flags; //4
uint32_t instanceAddressOffset; //4
uint32_t instanceSize;//4
uint16_t instanceAlignMask; //2
uint16_t reserved; //2
uint32_t classSize; //4
uint32_t classAddressOffset; //4
void *description;
// ...
};
Swift 属性
class Teacher {
var age: Int = 18;
var age2: Int = 20;
}
内存结构
可以看到如下的数据结构:
存储属性要么是常量存储(let 修饰)属性,要么是变量存储(var 修饰)属性:
class Teacher {
let age: Int = 18;
var name: String = "erge";
}
我们可以通过如下命令来查看 SIL 码:
swiftc -emit-sil main.swift | xcrun swift-demangle >> main.sil
# xcrun swift-demangle 将混淆的名称还原
SIL 码如下:
class Teacher {
@_hasStorage @_hasInitialValue final let age: Int { get }
@_hasStorage @_hasInitialValue var name: String { get set }
@objc deinit
init()
}
计算属性是不占用空间的
// Square 大小是 24
class Square {// 16
var width: Double = 8; // 8
// 计算属性:不占用内存空间 / 本质上就是 get 和 set 方法
var area: Double {
get { width * width}
set {
width = sqrt(newValue)
}
}
}
SIL 码如下:
class Square {
@_hasStorage @_hasInitialValue var width: Double { get set }
var area: Double { get set }
@objc deinit
init()
}
属性观察者
class Teacher {
var age: Int;
// 属性观察者
var name: String = "erge" {
// 新值存储之前
willSet {
print("willSet: \(newValue)");
}
// 新值存储之后
didSet {
print("didSet: \(oldValue)")
}
}
// 初始化期间不会调用 属性观察者
init() {
self.name = "haha";
self.age = 18;
}
}
注意:属性观察者在初始化期间是不会被调用的,原因在于初始化的时候,比如上面的代码,如果 name 的属性观察者中调用了 age 属性,那么得到的值是未知的,这不安全。
属性观察者继承
如下代码:
class Person {
var age: Int = 18;
// 属性观察者
var name: String {
// 新值存储之前
willSet {
print("willSet: \(newValue)");
}
// 新值存储之后
didSet {
print("didSet: \(oldValue)")
}
}
// 初始化期间不会调用 属性观察者
init() {
self.name = "haha";
self.age = 18;
}
}
class Student: Person {
override
var name: String {
willSet {
print("override: \(newValue)")
}
didSet {
print("override: \(oldValue)")
}
}
override init() {
super.init()
self.name = "xioaming"
}
}
结果如下:
override: xiaoming
willSet: xiaoming
didSet: haha
override: haha
注意:在继承的属性观察者中,初始化期间却被调用了,因为之前已经被初始化过了。
lazy
测试源码如下:
class Dog {
lazy var age:Int = 2
}
var d = Dog()
print(d.age);
可以通过内存查看访问前后值的变化:
为了更确切的了解底层实现,我们生成的SIL码:
class Dog {
lazy var age: Int { get set }
@_hasStorage @_hasInitialValue final var $__lazy_storage_$_age: Int? { get set }
@objc deinit
init()
}
从 SIL 码可以看出,lazy 修饰的属性其实会变为可选类型
当我们用 get 方法第一次去访问的时候:
所以:
class Dog {
// 16 + 9 = 25 ---> 32
lazy var age:Int = 2 // Optional
}
所以我们可以用:
print(class_getInstanceSize(Dog.self))
来查看 lazy 对类内存大小的影响:当设置为 lazy 修饰的时候类的大小为:32
非 lazy 修饰的时候大小为: 24
可以查看一下 **Optional
print(MemoryLayout<Optional<Int>>.size);
print(MemoryLayout<Optional<Int>>.stride);
所以总结一下,用 lazy 修饰的属性:
- 延迟存储属性必须有一个默认的初始值;
- 延迟存储在被第一次访问的时候才被赋值;
- 延迟存储属性并不能保证线程安全;
- 延迟存储属性对实例的大小有影响;
类属性
如下代码:
class Dog {
static var age:Int = 15
}
生成 SIL 码来查看具体的内容:
class Dog {
@_hasStorage @_hasInitialValue static var age: Int { get set }
@objc deinit
init()
}
// static Dog.age
sil_global hidden @static main.Dog.age : Swift.Int : $Int
由此可以看出,类属性是一个全局的静态变量。
我们分析一下访问的过程:
// 创建一个变量 d
alloc_global @main.d : Swift.Int // id: %2
// 将 %3 = d
%3 = global_addr @main.d : Swift.Int : $*Int // users: %17, %9
%4 = metatype $@thick Dog.Type
// function_ref Dog.age.unsafeMutableAddressor
// 去调用 函数 Dog.age.unsafeMutableAddressor
%5 = function_ref @main.Dog.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
// 将返回的值赋值给 %3
copy_addr %8 to [initialization] %3 : $*Int // id: %9
来看一下函数 Dog.age.unsafeMutableAddressor 的实现:
// one-time initialization function for age
// 这个函数的功能主要是赋值 15
sil private [global_init_once_fn] @one-time initialization function for age : $@convention(c) () -> () {
bb0:
alloc_global @static main.Dog.age : Swift.Int // id: %0
%1 = global_addr @static main.Dog.age : Swift.Int : $*Int // user: %4
%2 = integer_literal $Builtin.Int64, 15 // 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 'one-time initialization function for age'
// Dog.age.unsafeMutableAddressor
sil hidden [global_init] @main.Dog.age.unsafeMutableAddressor : Swift.Int : $@convention(thin) () -> Builtin.RawPointer {
bb0:
%0 = global_addr @one-time initialization token for age : $*Builtin.Word // user: %1
%1 = address_to_pointer %0 : $*Builtin.Word to $Builtin.RawPointer // user: %3
// function_ref one-time initialization function for age
// 调用 @one-time 函数进行初始化值 15
%2 = function_ref @one-time initialization function for age : $@convention(c) () -> () // user: %3
// 这里调用了 once
%3 = builtin "once"(%1 : $Builtin.RawPointer, %2 : $@convention(c) () -> ()) : $()
%4 = global_addr @static main.Dog.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.Dog.age.unsafeMutableAddressor : Swift.Int'
我们来看看 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
}
所以它很像是 objc 中的 dispatch_once 函数。
最终,类属性是线程安全的。
所以 Swift 中的单例:
class UserInfo {
static let instance = UserInfo()
private init() {}
}