Swift 底层原理初探
1. 编译原理
在iOS中我们经常使用Objective-C
和Swift
这两门语言进行编写代码,这两个都是高级语言。我们也都知道高级语言通过一些编译链接后,最终生成机器语言供我们的设备识别,结下来我们就探索一下Swift
这门语言是如何编译的。
首先我们来看一段代码,在这段代码中我创建了一个Teacher
类,并通过默认的初始化器创建了一个实例对象赋值给了t
class Teacher {
var age: Int = 18
var name:String = "abc"
}
let t = Teacher()
下面我们就来研究一下,这个默认的初始化器到底做了一个什么样的操作?这里我们引入SIL(Swift intermediate language)
,即Swift
中间语言,那么我们先来了解一下什么是SIL
。
iOS开发中不管是使用Objective-C
还是Swift
最后都是通过LLVM
进行编译的,如下图所示:
通过上图我们可以看到:
-
Objective-C
通过编译器,编译成IR
,然后在生成可执行文件.o
(也就是机器码) -
Swift
则是通过Swift
编译器编译成IR
,然后生成生成可执行文件.o
下面我们再来看一下,一个Swift
文件的编译过程都经历了那些步骤:
swift
在编译过程中使用的前端编译器是swiftc
,和我么之前在oc
中使用的clang
是有所区别的。我们可以通过如下命令查看swiftc
都能做什么事情:
swiftc -h
还有很多,截图截不全,感兴趣的可以自己执行命令去看看,如果想要详细的对SIL
的内容进行了解,可以参考苹果官方的这个视频。(PS:youtobe视频需要科学上网)
2. 通过SIL分析
SIL参考文档
我们新建一个Swift
命令行项目,编写如下代码:
class Teacher {
var age: Int = 18
var name:String = "abc"
}
let t = Teacher()
然后进入到这个目录,执行如下命令:
swiftc -emit-sil main.swift >> ./main.sil && open main.sil
如果打不开,可能是你没有设置用什么软件打开.sil
文件,设置一下就好了,我这里用的是VSCode
。
打开后如下:
Teacher 类
class Teacher {
@_hasStorage @_hasInitialValue var age: Int { get set }
@_hasStorage @_hasInitialValue var name: String { get set }
@objc deinit
init()
}
我们可以看到Teacher
类中:
- 有两个存储属性
- 一个析构方法
deinit
- 一个初始化函数
init()
这就是我们Teacher
类的在sil
文件中的所有信息。
看一下main函数代码
PS:去除代码混淆xcrun swift-demangle s4main1tAA7TeacherCvp
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer>>):
alloc_global @$s4main1tAA7TeacherCvp // id: %2
%3 = global_addr @$s4main1tAA7TeacherCvp : $*Teacher // user: %7
%4 = metatype $@thick Teacher.Type // user: %6
// function_ref Teacher.__allocating_init()
%5 = function_ref @$s4main7TeacherCACycfC : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %6
%6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %7
store %6 to %3 : $*Teacher // id: %7
%8 = integer_literal $Builtin.Int32, 0 // user: %9
%9 = struct $Int32 (%8 : $Builtin.Int32) // user: %10
return %9 : $Int32 // id: %10
} // end sil function 'main'
我们可以看到在我们的main.swift
文件中只有这里贴的那几行代码。但是编译后确有个main
函数,说明这里是隐藏了main
函数的,下面我们就对生成的.sil
文件中的main
函数进行分析。
-
@main
这里是标示我们当前main.swift
的入口函数,SIL
中的标示符以@
作为前缀 -
%0,%1……
在SIL
也叫寄存器,这里我们可以理解为我们日常开发的常量,一旦赋值之后就不可以在修改,如果SIL
中还要继续使用,那么就不断的累加数字。同事这里所说的寄存器是虚拟的,最终运行到我们的机器上才会使用真的寄存器 -
alloc_global
是创建一个局部变量 -
global_addr
是拿到局部变量的地址,赋值给%3
-
metatype
是拿到Teacher
的Metaldata
赋值给%4
- 接下来就是将
__allocating_init()
的函数地址赋值给%5
-
apply
是调用函数,这里是调用%5
也就是__allocating_init()
,%4
是参数,并将返回值给%6
- 然后将
%6
的值存储到%3
,也就是我们刚刚创建的全局变量的地址 - 然后是构建
Int
并return
Teacher.__allocating_init()
在main
函数中我们调用了Teacher.__allocating_init()
函数,那么我们就来看看这个函数。
// Teacher.__allocating_init()
sil hidden [exact_self_class] @$s4main7TeacherCACycfC : $@convention(method) (@thick Teacher.Type) -> @owned Teacher {
// %0 "$metatype"
bb0(%0 : $@thick Teacher.Type):
%1 = alloc_ref $Teacher // user: %3
// function_ref Teacher.init()
%2 = function_ref @$s4main7TeacherCACycfc : $@convention(method) (@owned Teacher) -> @owned Teacher // user: %3
%3 = apply %2(%1) : $@convention(method) (@owned Teacher) -> @owned Teacher // user: %4
return %3 : $Teacher // id: %4
} // end sil function '$s4main7TeacherCACycfC'
我们可以看到在__allocating_init
函数中:
-
%0
存储了metatype
(元类,也就是对象的类信息) -
%1
存储来了alloc_ref
(对象的引用计数) -
%2
是是Teacher
的init
函数 - 通过
apply
调用%2
存储的函数,也就是init
,并将返回值存储到%3
中 - 返回
%3
其实这里面还有很多方法,比如属性的getter
和setter
等等,这里放个文件的链接供大家参考:
链接: https://pan.baidu.com/s/1SQCscPJB8lneM0dLtsKF6Q
密码: ufvb
3. swift 对象&类 探索
在上面我们通过分析sil
文件可以知道,对象的初始会调用__allocating_init
函数,下面我们就开始探索。
3.1 引入
首先我们就添加个符号断点,看看一下它的汇编调用。
添加完符号断点后我们运行代码,运行后可以看到如下汇编结果:
通过上图我们可以知道:
- 在
__allocating_init
函数内部调用了swift_allocObject
- 然后还调用了
SwiftTest.Teacher.init()
3.2 探索对象的原理
从上面的汇编代码中我们知道在__allocating_init
函数内部调用了swift_allocObject
函数,下面我们直接来到编译好的Swift
源码中搜索swift_allocObject
,其实有很多,我们在前面加个_
就不多了,因为C++
通常会在函数中加个_
,如果没加也就只能慢慢找了。
swift_allocObject 源码:
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
assert(isAlignmentMask(requiredAlignmentMask));
auto object = reinterpret_cast(
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;
}
源码分析:
- 首先是断言判断
isAlignmentMask
,源码如下:
/// Is the given value a valid alignment mask?
static inline bool isAlignmentMask(size_t mask) {
// mask == xyz01111...
// mask+1 == xyz10000...
// mask&(mask+1) == xyz00000...
// So this is nonzero if and only if there any bits set
// other than an arbitrarily long sequence of low bits.
return (mask & (mask + 1)) == 0;
}
这里是判断内存对齐,如果mask & (mask + 1)
为true
说明对齐,否则就是不对齐,不对齐读出的数据就乱了,CPU
读取内存都是按段读的,这是一种空间换时间的概念。
- 接下来是通过
swift_slowAlloc
函数去初始化内存,这里是初始化了requiredSize
大小的一块内存给对象使用,源码如下:
// When alignMask == ~(size_t(0)), allocation uses the "default"
// _swift_MinAllocationAlignment. This is different than calling swift_slowAlloc
// with `alignMask == _swift_MinAllocationAlignment - 1` because it forces
// the use of AlignedAlloc. This allows manually allocated to memory to always
// be deallocated with AlignedFree without knowledge of its original allocation
// alignment.
//
// For alignMask > (_minAllocationAlignment-1)
// i.e. alignment == 0 || alignment > _minAllocationAlignment:
// The runtime must use AlignedAlloc, and the standard library must
// deallocate using an alignment that meets the same condition.
//
// For alignMask <= (_minAllocationAlignment-1)
// i.e. 0 < alignment <= _minAllocationAlignment:
// The runtime may use either malloc or AlignedAlloc, and the standard library
// must deallocate using an identical alignment.
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__)
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;
}
- 下面就是通过给对象的
metadata
赋值初始化一个HeapObject
类型的object
了 - 另外就是如果启用了泄漏跟踪,则开始跟踪此对象。
- 最后返回该对象
说了这么多我们就来看看返回的对象的类型HeapObject
吧。
// The members of the HeapObject header that are not shared by a
// standard Objective-C instance
#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 *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 LLVM_ATTRIBUTE_USED;
#endif
#endif // __swift__
};
- 可以看到
HeapObject
中油两个属性,一个metadata
,另一个是个宏定义,在上面的源码也已经列出来了其实就是refCounts
- 还有两个初始化方法,在这里用的是第一个
所以Swift
对象初始的调用流程是__allocating_init
->swift_allocObject
->_swift_allocObject
->_swift_slowAlloc
->Malloc
下面我们先来写一段代码,并且在该方法处添加一个断点,看看到底会不会走这个方法,并且初步看看这几个参数。
测试代码:
class Teacher {
var age:Int = 18
var name:String = "abc"
}
var t = Teacher()
回车运行后,结果如下图:
综上所述,对于Swift
对象的原理简单的概括如下:
- 对象初始化后一个
HeapObject
类型的object
- 初始化的
object
有两个属性:-
metadata
:元数据 -
refCount
:引用计数
-
- 另外我们还可以看到这个对象初始化的内存是40字节
-
metadata
占8字节 -
refCount
占8字节 -
age
属性占8字节 -
name
属性占16字节
-
至于为什么Int是8,String是16,这个我们后续在来介绍,这里就不展开说了,简单打印一下:
print(MemoryLayout.size)
print(MemoryLayout.size)
print(MemoryLayout.stride)
print(MemoryLayout.stride)
打印结果:
关于stride&size的解释:
/// The contiguous memory footprint of `T`, in bytes.
///
/// A type's size does not include any dynamically allocated or out of line
/// storage. In particular, `MemoryLayout.size`, when `T` is a class
/// type, is the same regardless of how many stored properties `T` has.
///
/// When allocating memory for multiple instances of `T` using an unsafe
/// pointer, use a multiple of the type's stride instead of its size.
public static var size: Int { get }
/// The number of bytes from the start of one instance of `T` to the start of
/// the next when stored in contiguous memory or in an `Array`.
///
/// This is the same as the number of bytes moved when an `UnsafePointer`
/// instance is incremented. `T` may have a lower minimal alignment that
/// trades runtime performance for space efficiency. This value is always
/// positive.
public static var stride: Int { get }
3.3 metadata 即 类原理探索
这一节我们就来分析一下上面提到metadata
。
在_swift_allocObject_
函数中我们可以看到metadata
的类型是HeapMetadata
,我们点击跳转过去:
#ifndef __swift__
#include
#include "swift/Basic/type_traits.h"
namespace swift {
struct InProcess;
template struct TargetHeapMetadata;
using HeapMetadata = TargetHeapMetadata;
#else
typedef struct HeapMetadata HeapMetadata;
typedef struct HeapObject HeapObject;
#endif
在这里我们可以看到HeapMetadata
是取自TargetHeapMetadata
的别名。我们跳转到``中可以看到如下源码:
/// 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
struct TargetHeapMetadata : TargetMetadata {
using HeaderType = TargetHeapMetadataHeader;
TargetHeapMetadata() = default;
constexpr TargetHeapMetadata(MetadataKind kind)
: TargetMetadata(kind) {}
#if SWIFT_OBJC_INTEROP
constexpr TargetHeapMetadata(TargetAnyClassMetadata *isa)
: TargetMetadata(isa) {}
#endif
};
- 在这里我们可以看到
TargetHeapMetadata
是一个模板的结构体 - 里面有两个初始化方法
- 一个是
swift
初始化的,初始化了一个kind
属性 - 另一个是
swift
与OC
互相操作的时候用的,这里初始化了isa
- 一个是
通过以上我们大概就能想到kind
应该跟isa
类似。那么在TargetHeapMetadata
中我们并没有找到kind
属性,那么应该在它的父类中吧,我们点击TargetMetadata
,如下:(省略后续代码)
/// The common structure of all type metadata.
template
struct TargetMetadata {
using StoredPointer = typename Runtime::StoredPointer;
/// The basic header type.
typedef TargetTypeMetadataHeader HeaderType;
constexpr TargetMetadata()
: Kind(static_cast(MetadataKind::Class)) {}
constexpr TargetMetadata(MetadataKind Kind)
: Kind(static_cast(Kind)) {}
#if SWIFT_OBJC_INTEROP
protected:
constexpr TargetMetadata(TargetAnyClassMetadata *isa)
: Kind(reinterpret_cast(isa)) {}
#endif
private:
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind;
public:
/// Get the metadata kind.
MetadataKind getKind() const {
return getEnumeratedMetadataKind(Kind);
}
/// Set the metadata kind.
void setKind(MetadataKind kind) {
Kind = static_cast(kind);
}
`
`
`
}
我们可以看到在TargetMetadata
中有一个私有的StoredPointer
类型的属性kind
,并且提供了kind
的get
和set
函数。StoredPointer
类型其实就是unsigned long
,这个代码在上面提到的HeapMetadata
取别名的InProcess
中。
using StoredPointer = typename Runtime::StoredPointer;
using StoredPointer = uintptr_t;
typedef unsigned long uintptr_t;
找了一圈,那么这个kind
到底有什么用呢?
我们点击get
方法中的返回值类型``来到如下代码处:
/// 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.def
文件,我们点击进去看看,文件中的代码如下:
//===--- MetadataKind.def ---------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This is a file that enables metaprogramming with metadata kinds.
//
//===----------------------------------------------------------------------===//
/// METADATAKIND(Name, Value)
/// Represents a swift native runtime metadata kind. Name is the Name of the
/// metadata kind and Value is the integral value used to identify the value.
#ifndef METADATAKIND
#define METADATAKIND(Name, Value)
#endif
/// ABSTRACTMETADATAKIND(Name, Start, End)
/// Represents an abstraction categorization of a range of metadata kind
/// values. Name is the identifier of the range and Start, End are the
/// beginning and end of the range.
#ifndef ABSTRACTMETADATAKIND
#define ABSTRACTMETADATAKIND(Name, Start, End)
#endif
/// NOMINALTYPEMETADATAKIND(Name, Value)
/// Represents the native metadata kind for a swift nominal type. Name is the
/// name of the kind and Value is the integral value used to identify the
/// value. Delegates to METADATAKIND if not defined.
#ifndef NOMINALTYPEMETADATAKIND
#define NOMINALTYPEMETADATAKIND(Name, Value) METADATAKIND(Name, Value)
#endif
/// A class type.
NOMINALTYPEMETADATAKIND(Class, 0)
/// A struct type.
NOMINALTYPEMETADATAKIND(Struct, 0 | MetadataKindIsNonHeap)
/// An enum type.
/// If we add reference enums, that needs to go here.
NOMINALTYPEMETADATAKIND(Enum, 1 | MetadataKindIsNonHeap)
/// An optional type.
NOMINALTYPEMETADATAKIND(Optional, 2 | MetadataKindIsNonHeap)
/// A foreign class, such as a Core Foundation class.
METADATAKIND(ForeignClass, 3 | MetadataKindIsNonHeap)
/// A type whose value is not exposed in the metadata system.
METADATAKIND(Opaque, 0 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// A tuple.
METADATAKIND(Tuple, 1 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// A monomorphic function.
METADATAKIND(Function, 2 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// An existential type.
METADATAKIND(Existential, 3 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// A metatype.
METADATAKIND(Metatype, 4 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// An ObjC class wrapper.
METADATAKIND(ObjCClassWrapper, 5 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// An existential metatype.
METADATAKIND(ExistentialMetatype, 6 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
/// A heap-allocated local variable using statically-generated metadata.
METADATAKIND(HeapLocalVariable, 0 | MetadataKindIsNonType)
/// A heap-allocated local variable using runtime-instantiated metadata.
METADATAKIND(HeapGenericLocalVariable,
0 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
/// A native error object.
METADATAKIND(ErrorObject,
1 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
// getEnumeratedMetadataKind assumes that all the enumerated values here
// will be <= LastEnumeratedMetadataKind.
#undef ABSTRACTMETADATAKIND
#undef NOMINALTYPEMETADATAKIND
#undef METADATAKIND
name | value |
---|---|
Class | 0x0 |
Struct | 0x200 |
Enum | 0x201 |
Optional | 0x202 |
ForeignClass | 0x203 |
Opaque | 0x300 |
Tuple | 0x301 |
Function | 0x302 |
Existential | 0x303 |
Metatype | 0x304 |
ObjCClassWrapper | 0x305 |
ExistentialMetatype | 0x306 |
HeapLocalVariable | 0x400 |
HeapGenericLocalVariable | 0x500 |
ErrorObject | 0x501 |
LastEnumerated | 0x7FF |
所以说这个kind
是指的我要具体初始化哪一类的元数据。下面我们就Class
类型进行详细分析:
那么我们的metadata
只有这一个kind
吗?通过对oc
的对比,应该不是这个样子的,我们继续从TargetMetadata
这个结构体中去寻找,我们找打如下函数:
/// Get the class object for this type if it has one, or return null if the
/// type is not a class (or not a class with a class object).
const TargetClassMetadata *getClassObject() const;
点击跳转找到其真正实现如下:
template<> inline const ClassMetadata *
Metadata::getClassObject() const {
switch (getKind()) {
case MetadataKind::Class: {
// Native Swift class metadata is also the class object.
return static_cast(this);
}
case MetadataKind::ObjCClassWrapper: {
// Objective-C class objects are referenced by their Swift metadata wrapper.
auto wrapper = static_cast(this);
return wrapper->Class;
}
// Other kinds of types don't have class objects.
default:
return nullptr;
}
}
我们可以看到如果kind
是Class
类型,就直接对this
(当前指针,即metadata
)强转为ClassMetadata
,ClassMetadata
是个别名,其实际是:
using ClassMetadata = TargetClassMetadata;
在这个getClassObject
函数中,其返回值是TargetClassMetadata
类型,对于Class
类型也被强转成了TargetClassMetadata
,所以我们点击跳转到TargetClassMetadata
中:(省略很多代码)
/// The structure of all class metadata. This structure is embedded
/// directly within the class's heap metadata structure and therefore
/// cannot be extended without an ABI break.
///
/// Note that the layout of this type is compatible with the layout of
/// an Objective-C class.
template
struct TargetClassMetadata : public TargetAnyClassMetadata {
using StoredPointer = typename Runtime::StoredPointer;
using StoredSize = typename Runtime::StoredSize;
TargetClassMetadata() = default;
constexpr TargetClassMetadata(const TargetAnyClassMetadata &base,
ClassFlags flags,
ClassIVarDestroyer *ivarDestroyer,
StoredPointer size, StoredPointer addressPoint,
StoredPointer alignMask,
StoredPointer classSize, StoredPointer classAddressPoint)
: TargetAnyClassMetadata(base),
Flags(flags), InstanceAddressPoint(addressPoint),
InstanceSize(size), InstanceAlignMask(alignMask),
Reserved(0), ClassSize(classSize), ClassAddressPoint(classAddressPoint),
Description(nullptr), IVarDestroyer(ivarDestroyer) {}
// The remaining fields are valid only when isTypeMetadata().
// The Objective-C runtime knows the offsets to some of these fields.
// Be careful when accessing them.
/// Swift-specific class flags.
ClassFlags Flags;
/// The address point of instances of this type.
uint32_t InstanceAddressPoint;
/// The required size of instances of this type.
/// 'InstanceAddressPoint' bytes go before the address point;
/// 'InstanceSize - InstanceAddressPoint' bytes go after it.
uint32_t InstanceSize;
/// The alignment mask of the address point of instances of this type.
uint16_t InstanceAlignMask;
/// Reserved for runtime use.
uint16_t Reserved;
/// The total size of the class object, including prefix and suffix
/// extents.
uint32_t ClassSize;
/// The offset of the address point within the class object.
uint32_t ClassAddressPoint;
`
`
`
}
在这类中我们就发现了很多属性(见上面的源码)。我们还能看见TargetClassMetadata
也是个模板类型的结构体,继承自TargetAnyClassMetadata
,部分代码如下:
/// The portion of a class metadata object that is compatible with
/// all classes, even non-Swift ones.
template
struct TargetAnyClassMetadata : public TargetHeapMetadata {
using StoredPointer = typename Runtime::StoredPointer;
using StoredSize = typename Runtime::StoredSize;
#if SWIFT_OBJC_INTEROP
constexpr TargetAnyClassMetadata(TargetAnyClassMetadata *isa,
TargetClassMetadata *superclass)
: TargetHeapMetadata(isa),
Superclass(superclass),
CacheData{nullptr, nullptr},
Data(SWIFT_CLASS_IS_SWIFT_MASK) {}
#endif
constexpr TargetAnyClassMetadata(TargetClassMetadata *superclass)
: TargetHeapMetadata(MetadataKind::Class),
Superclass(superclass),
CacheData{nullptr, nullptr},
Data(SWIFT_CLASS_IS_SWIFT_MASK) {}
#if SWIFT_OBJC_INTEROP
// Allow setting the metadata kind to a class ISA on class metadata.
using TargetMetadata::getClassISA;
using TargetMetadata::setClassISA;
#endif
// Note that ObjC classes does not have a metadata header.
/// The metadata for the superclass. This is null for the root class.
ConstTargetMetadataPointer Superclass;
// TODO: remove the CacheData and Data fields in non-ObjC-interop builds.
/// The cache data is used for certain dynamic lookups; it is owned
/// by the runtime and generally needs to interoperate with
/// Objective-C's use.
TargetPointer CacheData[2];
/// The data pointer is used for out-of-line metadata and is
/// generally opaque, except that the compiler sets the low bit in
/// order to indicate that this is a Swift metatype and therefore
/// that the type metadata header is present.
StoredSize Data;
`
`
`
}
我们发现TargetAnyClassMetadata
也是个模板结构体,继承自TargetHeapMetadata
,而TargetHeapMetadata
在上面我们就看到了,它继承自TargetMetadata
,这回基本就通了,集成关系如下:
TargetClassMetadata
: TargetAnyClassMetadata
: TargetHeapMetadata
: TargetMetadata
这其中:
-
TargetMetadata
中有1个属性:-
kind
:那种元类型
-
-
TargetHeapMetadata
中没有属性 -
TargetAnyClassMetadata
中油3个属性:-
Superclass
:执行父类的指针 -
CacheData
:缓存数据,缓存一些动态查找,用于OC
运行时 -
Data
:元数据头
-
-
TargetClassMetadata
中有8个-
Flags
:类标志 -
InstanceAddressPoint
:实例地址指针 -
InstanceSize
:该类型实例的所需大小 -
InstanceAlignMask
:此类型实例地址的对齐掩码 -
Reserved
:保留字段 -
ClassSize
:类对象总大小 -
ClassAddressPoint
:类对象的偏移量 -
Description
:类描述
-
所以Swift
类(Class
)的实际组成是:
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;
// ...
};
3.4 refCount
看到这个属性很自然的就会想到ARC
自动引用计数。Swift
同样使用ARC
。
refCount
类型是InlineRefCounts
typedef RefCounts InlineRefCounts;
可以看到InlineRefCounts
是RefCounts
的别名,RefCounts
是一个类,代码很长很长
在这里就先介绍到这里,后面的篇章会详细的介绍Swift
中的内存管理。
3.5 小结
- 在
Swift
中:- 对象的本质是一个
HeapObject
结构体,默认有两个属性(metadata
占8字节、refCount
占8字节),总体占用16字节 - 类信息存储在
metadata
元数据中 - 使用
ARC
自动引用计数管理,对象的引用
- 对象的本质是一个
- 在
Objective-C
中:- 对象的本质是一个
objc_object
的结构体,默认有一个isa
指针,占8字节 - 类信息存储在元类中
- 使用
ARC
自动引用计数管理,对象的引用
- 对象的本质是一个
4. Swift属性
在Swift
中属性主要分为存储属性、计算属性、延迟存储属性、类型属性,下面我们逐步分析。
1.1 存储属性
存储属性就是我们最常用的一种方式,这里也分两种:
- 常量存储属性,使用
let
修饰 - 变量存储属性,使用
var
修饰
下面我们通过SIL
来看看存储属性
Swift 代码:
class Teacher {
let age: Int = 18
var name:String = "abc"
}
let t = Teacher()
SIL 代码:
class Teacher {
@_hasStorage @_hasInitialValue final let age: Int { get }
@_hasStorage @_hasInitialValue var name: String { get set }
@objc deinit
init()
}