美文网首页
swift--类

swift--类

作者: Mjs | 来源:发表于2020-12-10 13:48 被阅读0次

    iOS开发的语⾔不管是 OC 还是 Swift 后端都是通过 LLVM 进⾏编译的,如下图所示:


    LLVM.png

    可以看到:

    OC 通过 clang 编译器,编译成IR,然后再⽣成可执⾏⽂件.o(这⾥也就是我们的机器码)

    Swift 则是通过 Swift编译器 编译成IR,然后在⽣成可执⾏⽂件。

    我们再来看⼀下,⼀个 ⽂件的编译过程都经历了哪些步骤:

    Swift编译过程.png
    swift 在编译过程中使⽤的前端编译器是 swiftc ,和我们之前在 OC 中使⽤的 Clang 是有所区别的。 我
    们可以通过如下命令查看 swiftc 都能做什么样的事情:
    swiftx -h
    USAGE: swiftc
    
    MODES:
      -dump-ast              Parse and type-check input file(s) and dump AST(s)
      -dump-parse            Parse input file(s) and dump AST(s)
      -dump-pcm              Dump debugging information about a precompiled Clang module
      -dump-scope-maps <expanded-or-list-of-line:column>
                             Parse and type-check input file(s) and dump the scope map(s)
      -dump-type-info        Output YAML dump of fixed-size types from all imported modules
      -dump-type-refinement-contexts
                             Type-check input file(s) and dump type refinement contexts(s)
      -emit-assembly         Emit assembly file(s) (-S)
      -emit-bc               Emit LLVM BC file(s)
      -emit-executable       Emit a linked executable
      -emit-imported-modules Emit a list of the imported modules
      -emit-ir               Emit LLVM IR file(s)
      -emit-library          Emit a linked library
      -emit-object           Emit object file(s) (-c)
      -emit-pcm              Emit a precompiled Clang module from a module map
      -emit-sibgen           Emit serialized AST + raw SIL file(s)
      -emit-sib              Emit serialized AST + canonical SIL file(s)
      -emit-silgen           Emit raw SIL file(s)
      -emit-sil              Emit canonical SIL file(s)
      -index-file            Produce index data for a source file
      -parse                 Parse input file(s)
      -print-ast             Parse and type-check input file(s) and pretty print AST(s)
      -resolve-imports       Parse and resolve imports in input file(s)
      -typecheck             Parse and type-check input file(s)
    

    SIL分析

    class Teacher{
        var age:Int = 18
        var name:String = "abc"
    }
    var t = Teacher()   //OC:alloc(内存分配) init(初始化)
    

    通过swiftc -emit-sil main.swift >> ./main.sil编译

    // t
    sil_global hidden @$s4main1tAA7TeacherCvp : $Teacher
    
    // main
    sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
    bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
      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 这⾥标识我们当前 main.swift 的⼊⼝函数,SIL 中的标识符名称以 @ 作为前缀
    • %0, %1... 在 SIL 也叫做寄存器,这⾥我们可以理解为我们⽇常开发中的常量,⼀旦赋值之后就不可以再修改,如果 SIL 中还要继续使⽤,那么就不断的累加数字。 同时这⾥所说的寄存器是虚拟的,最终运⾏到我们的机器上,会使⽤真的寄存器。
    • alloc_global创建⼀个全局变量
    • global_addr拿到全局变量的地址,赋值给 %3
    • metatype拿到TeacherMetadata 赋值给 %4
    • allocation_init的函数地址赋值给 %5
    • apply调⽤allocation_init, 并把返回值给 %6
    • 将 %6 的值存储到 %3(也就是我们刚刚创建的全局变量的地址)
    • 构 建 Int , 并 return
      alloc_global @$s4main1tAA7TeacherCvp 定义一个全局的变量,我们可以通过命令xcrun swift-demangle s4main1tAA7TeacherCvp查看其实就是下面
    $s4main1tAA7TeacherCvp ---> main.t : main.Teacher
    

    %0%1相当于虚拟的寄存器。相当于常量。所以这段意思也就是5%为初始化方法,将元类型放进去,然后得出结果6%放入全局变量3%。初始化一个实例变量地址赋值给全局变量。
    5%的方法s4main7TeacherCACycfC

    // Teacher.__allocating_init()
    sil hidden [exact_self_class] [ossa] @$s4main7TeacherCACycfC : $@convention(method) (@thick Teacher.Type) -> @owned Teacher {
    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方法,在xcode中添加条件断点。

    
        0x100001c04 <+20>: callq  0x100001d58               ; symbol stub for: swift_allocObject
        0x100001c09 <+25>: movq   %rax, %r13
        0x100001c0c <+28>: callq  0x100001c40               ; swiftTest.Teacher.init() -> swiftTest.Teacher at main.swift:11
    

    __allocating_init里调用了swift_allocObject
    我们去swift源码中找到 _swift_allocObject方法

    _swift_allocObject_.png
    开辟空间,根据metadata创建HeapObject对象
      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)  //引用计数
      { }
      
    

    Swift对象的内存结构HeapObject,有两个属性: ⼀个是Metadata,⼀个是RefCount,默认占⽤16字节⼤⼩
    Int 8字节

    print(MemoryLayout<String>.stride)
    ·····················
    16
    

    我们可以看到String占用的内存时16
    所以最后占用了40字节大小。

    类结构探索

    using HeapMetadata = TargetHeapMetadata<InProcess>;
    

    HeapMetadata其实是TargetHeapMetadata,而TargetHeapMetadata里面并没有属性,找到他的父类TargetMetadata

    private:
      /// The kind. Only valid for non-class metadata; getKind() must be used to get
      /// the kind value.
      StoredPointer Kind;
    

    只有一个属性StoredPointerkind,这里记录这什么类型的元数据。
    从方法里去寻找

    
      /// 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;
    

    继承链为TargetClassMetadata -->TargetAnyClassMetadata-->TargetHeapMetadata-->TargetMetadata

    经过源码的阅读,我们应该能得出当前 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 11
    uint32_t classSize; //4
    uint32_t classAddressOffset; //4
    void *description; 
        // ...
     };
    

    相关文章

      网友评论

          本文标题:swift--类

          本文链接:https://www.haomeiwen.com/subject/ckwzwktx.html