Swift进阶(二)--- 类,对象

2020-12-13  本文已影响0人  Jax_YD

Swift编译简介

我们先来开一段简单的代码:

class SuperMan  {
    var age:Int = 18
    var name:String = "Aaron"
}
let t = SuperMan()

我们创建一个SuperMan类,并通过默认的初始化器,创建一个实例对象并赋值给了t
那么问题来了:这个默认的初始化器到底做了一个什么样的操作?
这里我们引入SIL(Swift intermediate language),在阅读SIL的代码之前,我们先来了解一下什么是SIL

首先不管是OC还是Swift,后端都是通过LLVM进行编译的,如下图所示:

image.png
可以看到:
OC通过clang编译器,编译成IR,然后再生成可执行文件.o(这里也就是我们的机器码)
Swift则是通过Swift 编译器编译成IR,然后再生成可执行文件
我们再来看一下,一个swift文件的编译过程都经历了哪些步骤:
image.png

swift在编译过程中使用的前端编译器是swiftc,和我们之前在OC中使用的Clang是有所区别的。我们可以通过swiftc -h命令,来查看swiftc都能做什么事情

image.png

SIL

SIL参考文档

// t
sil_global hidden @main.t : main.SuperMan : $SuperMan

// main
// '@main': 标识当前main.swift的入口函数。SIL中的标识符名称以‘@’作为前缀
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
//'%0、%1' 在SIL中叫做寄存器,可以理解为开发中的常量,一旦赋值就不可修改,如果想继续使用,就需要不算的累加数字(注意:这里的寄存器指的是‘虚拟寄存器’,与汇编指令‘register read %x’读取的寄存器不同,汇编指令读取的是真实的寄存器)
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
//‘alloc_global’ 创建一个‘全局变量’ --> ‘t
  alloc_global @main.t : main.SuperMan           // id: %2
//‘global_addr’ 获取全局变量的地址,并赋值给%3
  %3 = global_addr @main.t : main.SuperMan : $*SuperMan // user: %7
//‘metatype’ 获取‘Superman’ 的 ‘MetaData’ 赋值给 %4
  %4 = metatype $@thick SuperMan.Type             // user: %6
  // function_ref SuperMan.__allocating_init()
//将 ‘__allocating_init()’ 函数的地址赋值给 %5
  %5 = function_ref @main.SuperMan.__allocating_init() -> main.SuperMan : $@convention(method) (@thick SuperMan.Type) -> @owned SuperMan // user: %6
//’apply‘ 调用 ’__allocating_init()‘ 并将返回值 赋值给 %6  
  %6 = apply %5(%4) : $@convention(method) (@thick SuperMan.Type) -> @owned SuperMan // user: %7
//’store‘ 将 %6 的值 存储到 %3,也就是前面提到的全局变量的地址
  store %6 to %3 : $*SuperMan                     // id: %7
//创建一个整数文字值,类型为Builtin.Int32,该类型必须为内置整数类型。文字值是使用Swift的整数文字语法指定的,值为0,使用者%8
  %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'

源码调试

根据上面的分析,接下来我们通过swift_allocObject来探索swift中对象的创建过程,通过搜索swift_allocObject我们找到了该函数的实现

image3.png

swift_allocObject 源码分析

swift_allocObject的源码如下,主要有一下几部分组成

typedef RefCounts<InlineRefCountBits> InlineRefCounts;
image8.png

总结

看到这里,有的同学可能会有一些疑问❓:为什么 agename分别是 8字节16字节?
接下来我们详细讲解一下:
对于IntString类型,在Swift中,两个都是结构体类型。我可以通过内存打印来验证一下

//********* Int底层定义 *********
@frozen public struct Int : FixedWidthInteger, SignedInteger {...}

//********* String底层定义 *********
@frozen public struct String {...}

//********* 验证 *********
print("Int类型所占内存大小:\(MemoryLayout<Int>.stride)")
print("String类型所占内存大小:\(MemoryLayout<String>.stride)")

print("t.age所占内存大小:\(MemoryLayout.size(ofValue: t.age))")
print("t.name所占内存大小:\(MemoryLayout.size(ofValue: t.name))")
//********* 打印结果 *********
Int类型所占内存大小:8
String类型所占内存大小:16
t.age所占内存大小:8
t.name所占内存大小:16

通过上面的打印结果,可以清楚的看到IntString所占内存大小。

但是到这里,可能同学问了,那如果String的值非常大的话,内存里面分配的16个自己又该怎么存储呢?
swift的内存分配的问题,之后会单独写一篇文章来详细讲一下,这里就不做详细的赘述,这里记住Int占8个字节,String占16个字节就可以了。其它的类型,可自行打印出来看一下。

下面我们来看另一个问题。我们上面提到了metadata,那它到底是什么呢?我们继续往下分析

Swift中 类结构的探索

在OC中类是从objc_class模板继承过来的。
在Swift中,类的结构在底层是HeapObject,包含metadatarefCounts
metadata的类型是HeapMetadata

HeapMetadata类型分析

using HeapMetadata = TargetHeapMetadata<InProcess>
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
};
//******** TargetMetaData 定义 ********
struct TargetMetadata {
  using StoredPointer = typename Runtime::StoredPointer
  ...
  private:
  /// The kind. Only valid for non-class metadata; getKind() must be used to get
  /// the kind value.
  StoredPointer Kind
}
//******** Inprocess 定义 ********
struct InProcess {
  static constexpr size_t PointerSize = sizeof(uintptr_t);
  using StoredPointer = uintptr_t;
  ...
}
//******** uintptr_t 定义 ********
typedef unsigned long           uintptr_t;
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
  /// 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<Runtime> *getClassObject() const

//*************  方法的实现 **************
  template<> inline const ClassMetadata *
  Metadata::getClassObject() const {
    //匹配kind
    switch (getKind()) {
    //如果kind是class
    case MetadataKind::Class: {
      // Native Swift class metadata is also the class object.
      // 将当前指针强转为ClassMetadata类型
      return static_cast<const ClassMetadata *>(this);
    }
    case MetadataKind::ObjCClassWrapper: {
      // Objective-C class objects are referenced by their Swift metadata wrapper.
      auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
      return wrapper->Class;
    }
    // Other kinds of types don't have class objects.
    default:
      return nullptr;
    }
  
//************** ClassMetadata ****************
using ClassMetadata = TargetClassMetadata<InProcess>;

所以,TargetMetadataTargetClassMetadata 本质上是一样的,因为在内存结构中,可以直接进行指针的转换,所以我们可以认为:结构体其实就是TargetClassMetadata

template <typename Runtime>
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
...
/// Swift-specific class flags. 
///Swift 特有的标识
  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;
...
}
template <typename Runtime>
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
  using StoredPointer = typename Runtime::StoredPointer;
  using StoredSize = typename Runtime::StoredSize
...
}

总结

综上所述,当metadatakind为Class时,有如下继承关系:

image10.png
  • 当前类返回的实际类型是TargetClassMetadata;从上面继承链可以看出 TargetMetadata只有一个属性kindTargetAnyClassMetaData中有4个属性:kindsuperClasscacheDatadata
struct swift_class_t:NSObject {
    void *kind;//相当于OC中的isa,kind的实际类型是unsigned long
    void *superClass;
    void *cacheData;
    void *data;
    uint32_t flages; //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
    ...
}

与OC对比

上一篇下一篇

猜你喜欢

热点阅读