1. 类与结构体的异同:
类的定义
class PersonClass {
/* 1.类中可以定义存储值的属性 */
// 定义String 类型属性 name,初始值为 “Onion_Knight”
var name: String = "Onion_Knight"
// 定义Int 类型属性 age,初始值为 25
var age: Int = 25
/* 2.类中可以定义方法 */
// 定义方法 run
func run() {
print("Person is running")
}
/* 3.类中可以定义初始化器 */
// 初始化器 (Initializer)
// 必要初始化器 (Required Initializer)
required init(name: String, age: Int) {
self.name = name
self.age = age
}
// 便捷初始化器 (Convenience Initializer)
convenience init(name: String) {
let defaultAge: Int = 8
self.init(name: name, age: defaultAge)
}
// TODO: 定义下标,extension,conform to protocol
}
结构体的定义
struct PersonStruct {
/* 1.结构体中可以定义存储值的属性 */
var name: String = "Onion_Knight"
var age: Int = 25
/* 2.结构体中可以定义方法 */
func run() {
print("Person is running")
}
/* 3.结构体中可以定义初始化器, 但这里与类有所不同 */
// 初始化器 (Initializer)
init(name: String, age: Int) {
self.name = name
self.age = age
}
// TODO: 定义下标,extension,conform to protocol
}
这里我们更加关注结构体与类的不同点:
**类是引用类型,结构体是值类型。**因此引用计数是对于类来说的。
类有继承的特性, 而结构体没有。
类型转换可以在运行时检查和解释类实例的类型,而结构体不可以。
类有析构函数,而结构体不可以定制析构函数。
针对类的引用类型, 结构体的值类型:
// 快捷打印指针
func printPointer<T>(_ ptr: UnsafePointer<T>) {
print(ptr)
}
var personClassInstance = PersonClass(name: "OKClass", age: 18)
var personClassInstance_1 = personClassInstance
var personStructInstance = PersonStruct(name: "OKStruct", age: 19)
var personStructInstance_1 = personStructInstance
personClassInstance.age = 0
personStructInstance.age = 0
print(personClassInstance_1.age) // 0
print(personStructInstance_1.age) // 19
/* 通过 frame variable -L 查看各实例内存信息如下
(lldb) frame variable -L personClassInstance
scalar: (Swift_Programming.PersonClass) personClassInstance = 0x000000010041b3a0 {
0x000000010041b3b0: name = "OKClass"
0x000000010041b3c0: age = 0
}
(lldb) frame variable -L personClassInstance_1
scalar: (Swift_Programming.PersonClass) personClassInstance_1 = 0x000000010041b3a0 {
0x000000010041b3b0: name = "OKClass"
0x000000010041b3c0: age = 0
}
(lldb) frame variable -L personStructInstance
0x0000000100008380: (Swift_Programming.PersonStruct) personStructInstance = {
0x0000000100008380: name = "OKStruct"
0x0000000100008390: age = 0
}
(lldb) frame variable -L personStructInstance_1
0x0000000100008398: (Swift_Programming.PersonStruct) personStructInstance_1 = {
0x0000000100008398: name = "OKStruct"
0x00000001000083a8: age = 19
}
*/
修改personClassInstance
的age,personClassInstance_1
的age也发生了改变,而struct不同,正是因为类为引用类型,而结构体是值类型。而通过frame variable -L
获取帧栈信息更是切实印证了这一点。
也正是因为类为引用类型,结构体是值类型,一般情况下(不考虑全局/静态等特殊情况),类实例存储在堆区,结构体实例存储在栈区。
由于堆区空间的创建和销毁要比栈区更耗费时间,因此在不需要类的特性时,应当尽量使用结构体替代类以优化性能。(详见结构体与类的性能测试)。
2. 类的初始化器:
class PersonClass {
var name: String = "Onion_Knight"
var age: Int = 25
// 指定初始化器 (Designated Initializer) + 可失败 ?
init?(name: String, age: Int, minAge: Int) {
if minAge > age {
return nil
}
self.name = name
self.age = age
}
// 指定初始化器 (Designated Initializer)
required init(name: String, age: Int) {
self.name = name
self.age = age
}
// 便捷初始化器 (Convenience Initializer) + 必需 + 可失败
convenience required init?(name: String) {
let defaultAge: Int = 8
self.init(name: name, age: defaultAge)
}
// 便捷初始化器 (Convenience Initializer)
convenience init(age: Int) {
let defaultName: String = "OK君"
self.init(name: defaultName, age: age)
}
}
初始化器大体分为两类:指定初始化器 和 便捷初始化器
-
指定初始化器:
A: 必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性
都要初始化完成。
B: 必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如
果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖。
-
便捷初始化器:
A: 必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括 同类里定义的属性)。如果没这么做,便捷构初始化器赋予的新值将被自己类中其它指定初始化器所覆盖。
初始化器在第一阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用 self 作为值。
在初始化器上还可以通过添加
required
或者?
让初始化器成为 可失败初始化器 和 * 必要初始化器*。-- 可失败初始化器: 意味着当前因为参数的不合法或者外部条件 的不满足,存在初始化失败的情况。这种 Swift 中可失败初始化器写 return nil 语句, 来表明可失败初始化器在何种情况下会触发初始化失败。
-- 必要初始化器: 在类的初始化器前添加 required 修饰符来表明所有该类的子类都必须 实现该初始化器
3. 类的生命周期 -- 初始化:
iOS开发的语言不管是OC还是Swift后端都是通过LLVM进行编译的。
OC 通过 clang 编译器,编译成 IR,然后再生成可执行文件 .o (这里也就是我们的机器码)
Swift 则是通过 Swift 编译器编译成 IR,然后在生成可执行文件。
如下图所示:
Swift中具体过程如下:
Swift 代码 -> Parse 语法分析为 AST (抽象语法树) -> Sema 语义分析 -> 生成 SIL (Swift 中间语言)-> Raw SIL -> SILOpt Canonical SIL (优化过) -> IRGen -> LLVM IR -> Machine Code
// 分析输出AST
swiftc main.swift -dump-parse
// 分析并且检查类型输出AST
swiftc main.swift -dump-ast
// 生成中间体语言(SIL),未优化
swiftc main.swift -emit-silgen
// 生成中间体语言(SIL),优化后的
swiftc main.swift -emit-sil
// 生成LLVM中间体语言 (.ll文件)
swiftc main.swift -emit-ir
// 生成LLVM中间体语言 (.bc文件)
swiftc main.swift -emit-bc
// 生成汇编
swiftc main.swift -emit-assembly
// 编译生成可执行.out文件
swiftc -o main.o main.swift
SIL 分析:
有如下swift源码(main.swift)
class SimpleClass {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let simple = SimpleClass(name: "OK", age: 18)
通过swiftc main.swift -emit-sil > ./main.sil
生成.sil
文件
详见 SIL语言官方文档
分析类的初始化流程 注意到如下代码 (__allocating_init)
// SimpleClass.__allocating_init(name:age:)
sil hidden [exact_self_class] @$s4main11SimpleClassC4name3ageACSS_SitcfC : $@convention(method) (@owned String, Int, @thick SimpleClass.Type) -> @owned SimpleClass {
// %0 "name" // user: %5
// %1 "age" // user: %5
// %2 "$metatype"
bb0(%0 : $String, %1 : $Int, %2 : $@thick SimpleClass.Type):
%3 = alloc_ref $SimpleClass // user: %5
// function_ref SimpleClass.init(name:age:)
%4 = function_ref @$s4main11SimpleClassC4name3ageACSS_Sitcfc : $@convention(method) (@owned String, Int, @owned SimpleClass) -> @owned SimpleClass // user: %5
%5 = apply %4(%0, %1, %3) : $@convention(method) (@owned String, Int, @owned SimpleClass) -> @owned SimpleClass // user: %6
return %5 : $SimpleClass // id: %6
} // end sil function '$s4main11SimpleClassC4name3ageACSS_SitcfC'
这里的 SimpleClass.Type 作用类似于 OC 中的 isa.
在代码中通过汇编跟进, 发现
继续跟进:
作为一个纯粹的swift类(没有继承关系),主要流程:__allocating_init
-> swift_allocObject
申请并分配内存 -> init
初始化
对于继承了NSObject
的类,如下图:
其根源还是通过objc_allocWithZone
分配内存空间,通过objc_msgSend
的消息机制初始化的.
下面我们看一看swift_allocObject
, 都做了些什么。
通过官方资源下载Swift源码, 通过源码HeapObject.cpp分析:
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;
}
继续跟进swift_slowAlloc
:
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
void *p;
// This check also forces "default" alignment to use AlignedAlloc.
if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC
p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
p = malloc(size);
#endif
} else {
size_t alignment = (alignMask == ~(size_t(0)))
? _swift_MinAllocationAlignment
: alignMask + 1;
p = AlignedAlloc(size, alignment);
}
if (!p) swift::crash("Could not allocate memory.");
return p;
}
注意到malloc
相关的内存分配在这里发生了。
回到HeapObject.cpp,注意到返回的对象类型为HeapObject
, 所以Swift 对象的内存结构就是HeapObject
。
跟进HeapObject
,在Heap.h 可以看到HeapObject
的结构体:
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
/// The Swift heap-object header.
/// This must match RefCountedStructTy in IRGen.
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *__ptrauth_objc_isa_pointer metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
#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 SWIFT_USED;
#endif
#endif // __swift__
};
跟进InlineRefCounts refcount
, 在 RefCount.h 中发现
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
class RefCounts {
std::atomic<RefCountBits> refCounts;
// Out-of-line slow paths.
SWIFT_NOINLINE
void incrementSlow(RefCountBits oldbits, uint32_t inc) SWIFT_CC(PreserveMost);
SWIFT_NOINLINE
void incrementNonAtomicSlow(RefCountBits oldbits, uint32_t inc);
SWIFT_NOINLINE
bool tryIncrementSlow(RefCountBits oldbits);
SWIFT_NOINLINE
bool tryIncrementNonAtomicSlow(RefCountBits oldbits);
SWIFT_NOINLINE
void incrementUnownedSlow(uint32_t inc);
public:
enum Initialized_t { Initialized };
enum Immortal_t { Immortal };
// RefCounts must be trivially constructible to avoid ObjC++
// destruction overhead at runtime. Use RefCounts(Initialized)
// to produce an initialized instance.
RefCounts() = default;
// Refcount of a new object is 1.
constexpr RefCounts(Initialized_t)
: refCounts(RefCountBits(0, 1)) {}
// Refcount of an immortal object has top
......
}
// TODO: 推测出InlineRefCounts 本质是两个UInt32的数据结构的组合。
在这里面 metadata
和 refCount` 各占8字节,因此默认Swift对象默认占用16字节大小。
接下来继续跟进HeapMetaData
的结构, MetaData.h 中:
/// The common structure of all metadata for heap-allocated types. A
/// pointer to one of these can be retrieved by loading the 'isa'
/// field of any heap object, whether it was managed by Swift or by
/// Objective-C. However, when loading from an Objective-C object,
/// this metadata may not have the heap-metadata header, and it may
/// not be the Swift type metadata for the object's dynamic type.
template <typename Runtime>
struct TargetHeapMetadata : TargetMetadata<Runtime> {
using HeaderType = TargetHeapMetadataHeader<Runtime>;
TargetHeapMetadata() = default;
constexpr TargetHeapMetadata(MetadataKind kind)
: TargetMetadata<Runtime>(kind) {}
#if SWIFT_OBJC_INTEROP
constexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa)
: TargetMetadata<Runtime>(isa) {}
#endif
};
using HeapMetadata = TargetHeapMetadata<InProcess>;
在这里发现MetadataKind kind
与 isa
本质应该很相似。
/// Kinds of Swift metadata records. Some of these are types, some
/// aren't.
enum class MetadataKind : uint32_t {
#define METADATAKIND(name, value) name = value,
#define ABSTRACTMETADATAKIND(name, start, end) \
name##_Start = start, name##_End = end,
#include "MetadataKind.def"
/// The largest possible non-isa-pointer metadata kind value.
///
/// This is included in the enumeration to prevent against attempts to
/// exhaustively match metadata kinds. Future Swift runtimes or compilers
/// may introduce new metadata kinds, so for forward compatibility, the
/// runtime must tolerate metadata with unknown kinds.
/// This specific value is not mapped to a valid metadata kind at this time,
/// however.
LastEnumerated = 0x7FF,
};
MetadataKind
本质就是一个 uint32_t
:
struct TargetMetadata {
using StoredPointer = typename Runtime::StoredPointer;
/// The basic header type.
typedef TargetTypeMetadataHeader<Runtime> HeaderType;
constexpr TargetMetadata()
: Kind(static_cast<StoredPointer>(MetadataKind::Class)) {}
constexpr TargetMetadata(MetadataKind Kind)
: Kind(static_cast<StoredPointer>(Kind)) {}
#if SWIFT_OBJC_INTEROP
protected:
constexpr TargetMetadata(TargetAnyClassMetadata<Runtime> *isa)
: Kind(reinterpret_cast<StoredPointer>(isa)) {}
#endif
private:
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind;
......
getTypeContextDescriptor() const {
switch (getKind()) {
case MetadataKind::Class: {
const auto cls = static_cast<const TargetClassMetadata<Runtime> *>(this);
if (!cls->isTypeMetadata())
return nullptr;
if (cls->isArtificialSubclass())
return nullptr;
return cls->getDescription();
}
case MetadataKind::Struct:
case MetadataKind::Enum:
case MetadataKind::Optional:
return static_cast<const TargetValueMetadata<Runtime> *>(this)
->Description;
case MetadataKind::ForeignClass:
return static_cast<const TargetForeignClassMetadata<Runtime> *>(this)
->Description;
default:
return nullptr;
}
}
......
}
StoredPointer Kind
中保存的就是刚刚的Kind数据。
注意到getKind()
为Class
的分支的cls,由TargetClassMetadata
转化而来,
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;
using StoredSize = typename Runtime::StoredSize;
TargetClassMetadata() = default;
constexpr TargetClassMetadata(const TargetAnyClassMetadata<Runtime> &base,
ClassFlags flags,
ClassIVarDestroyer *ivarDestroyer,
StoredPointer size, StoredPointer addressPoint,
StoredPointer alignMask,
StoredPointer classSize, StoredPointer classAddressPoint)
: TargetAnyClassMetadata<Runtime>(base),
Flags(flags), InstanceAddressPoint(addressPoint),
InstanceSize(size), InstanceAlignMask(alignMask),
Reserved(0), ClassSize(classSize), ClassAddressPoint(classAddressPoint),
Description(nullptr), IVarDestroyer(ivarDestroyer) {}
......
}
再跟进TargetAnyClassMetaData
:
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;
using StoredSize = typename Runtime::StoredSize;
#if SWIFT_OBJC_INTEROP
constexpr TargetAnyClassMetadata(TargetAnyClassMetadata<Runtime> *isa,
TargetClassMetadata<Runtime> *superclass)
: TargetHeapMetadata<Runtime>(isa),
Superclass(superclass),
CacheData{nullptr, nullptr},
Data(SWIFT_CLASS_IS_SWIFT_MASK) {}
#endif
constexpr TargetAnyClassMetadata(TargetClassMetadata<Runtime> *superclass)
: TargetHeapMetadata<Runtime>(MetadataKind::Class),
Superclass(superclass)
#if SWIFT_OBJC_INTEROP
, CacheData{nullptr, nullptr},
Data(SWIFT_CLASS_IS_SWIFT_MASK)
#endif
{}
......
}
就可以推测出MetaData
的数据结构如下:
struct Metadata {
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
回到main.swift, 添加如下代码:
struct HeapObject {
var metaData: UnsafeRawPointer
var refcount1: UInt32
var refcount2: UInt32
}
struct Metadata {
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
class SimpleClass {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let simple = SimpleClass(name: "OK", age: 18)
let rawPtr = Unmanaged.passUnretained(simple as AnyObject).toOpaque()
let ptr = rawPtr.bindMemory(to: HeapObject.self, capacity: 1)
let metadata = ptr.pointee.metaData.bindMemory(to: Metadata.self, capacity: MemoryLayout<Metadata>.stride).pointee
print(metadata)
print("end")
可以看到metadata的内存结构经过内存绑定之后如下图:
可见Metadata 的结构确实如此.