美文网首页OC底层
OC 类&类结构分析

OC 类&类结构分析

作者: H丶ym | 来源:发表于2020-09-18 15:17 被阅读0次

    OC底层原理学习

    学习OC时,常听的就是万物皆对象,对象都有isa指针,那为什么有isa指针,isa指针到底是谁创建的?怎么创建的?我们来探索一下

    探索的方法

    使用Clang或者xcrun.m文件转换成.cpp文件,通过.cpp文件观察底层的方法和实现
    Clang是由苹果主导编写,基于LLVMC/C++/OC的编译器
    xcode安装的时候顺带安装了xcrun命令、xcrun命令在Clang的基础上进行了一些封装,要更好一些
    打开终端,cd到当前文件下,利用以下几种命令行可以编译成.cpp文件
    创建一个继承于NSObjectPerson类,添加一个字符串name属性

    //使用xcode工具 xcrun
    //1、指定模拟器文件编译
    xcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc Person.m -o Person.cpp
    
    //2、指定真机文件编译
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person.m -o Person-arm64.cpp
    
    //使用Clang 将 main.m 编译成 main.cpp
    clang -rewrite-objc main.m -o main.cpp
    

    objc_class& objc_object

    打开编译好的.cpp文件,内容多的不知道从何入手了,不急,一步步来

    1. 搜索我们的类名Person,看看能发现啥

    我找了OC的源码,但是被注释掉了,在它的上面发现了Person_IMPL这个结构体,它包含了我们的属性name,由此可证明,我的Person类被编译成了结构体,这个结构体中,不仅包含了name,还有一个struct NSObject_IMPL NSObject_IVARS;找到NSObject_IMPL的定义

    1. 打开我们之前编译好的objc4-781的源码,搜索Class isa,cmd Class找到它的定义
    1. cmd objc_class,来到 objc_class的源码

    objc_class 没有isa啊,进它的父类objc_object看看
    • 类的本质是一个结构体,有一个objc_class类型的isa属性
    • objc_class继承于另一个结构体objc_object,类的isa属性是从objc_object继承过来的
    • objc_object 就是根源类

    objc_class结构分析

    struct objc_class : objc_object {
        // Class ISA; //8字节
        Class superclass; //Class 类型 8字节
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
        //....方法部分省略
    }
    
    • isa:继承于objc_objectisa,8字节
    • superclass:父类,本质objc_class结构体指针, 8字节
    • cache: 缓存指针和函数表(下一章深入探索)
    • bits: 属性、方法等信息 (一会深入探索)

    cache 内存大小

    cachecache_t类型,看下cache_t的源码

    • buckets 类型是struct bucket_t *,是结构体指针类型,占8字节
    • maskmask_t类型,而mask_tunsigned int 的别名,占4字节
    • _maskAndBucketsuintptr_t类型,它是一个指针,占8字节
    • _mask_unusedmask_t类型,而mask_tuint32_t 类型定义的别名,占4字节

    无论走if还是 else if 都是16字节

    通过指针偏移读取类内存中bits 中的信息

    bits = 的首地址偏移32字节
    上一小节中我们提到bits存储的属性、方法等信息,现在我们添加一个成员变量、一个类方法、一个对象方法

    接下来打印一下bits

    step 1. 获取类地址

    (lldb) p/x person.class
    (Class) $0 = 0x00000001000022d8 Person
    

    step 2. 首地址偏移32字节,32字节转16进制 20
    0x00000001000022d8 -> 0x00000001000022f8,打印时,强转成bits的类型,强转以后我们才能调用class_data_bits_t的方法

    (lldb) p (class_data_bits_t *)0x00000001000022f8
    (class_data_bits_t *) $1 = 0x00000001000022f8
    

    step 3. 调用class_data_bits_t提供的data()方法,获取class_rw_t* data,看下data的信息

    (lldb) p $1->data()
    (class_rw_t *) $2 = 0x0000000102004680
    (lldb) p *$2
    (class_rw_t) $3 = {
      flags = 2148007936
      witness = 1
      ro_or_rw_ext = {
        std::__1::atomic<unsigned long> = 4294975800
      }
      firstSubclass = nil
      nextSiblingClass = NSUUID
    }
    

    暂时看不懂,class_rw_t提供了一个方法methods(),看起来应该是获取方法列表

    step 4.调用methods,看下输出

    (lldb) p $3.methods()
    (const method_array_t) $4 = {
      list_array_tt<method_t, method_list_t> = {
         = {
          list = 0x0000000100002180
          arrayAndFlag = 4294975872
        }
      }
    }
    

    返回的是method_array_t 继承于 list_array_tt,使用 list_array_tt提供的list属性,打印

    (lldb) p $4.list
    (method_list_t *const) $5 = 0x0000000100002180
    (lldb) p *$5
    (method_list_t) $6 = {
      entsize_list_tt<method_t, method_list_t, 3> = {
        entsizeAndFlags = 26
        count = 4
        first = {
          name = "objcMethod"
          types = 0x0000000100000f80 "v16@0:8"
          imp = 0x0000000100000dd0 (Example`-[Person objcMethod])
        }
      }
    }
    

    count = 4,证明我们有4个方法,分别打印一下其它的方法

    (lldb) p $6.get(0)
    (method_t) $7 = {
      name = "objcMethod"
      types = 0x0000000100000f80 "v16@0:8"
      imp = 0x0000000100000dd0 (Example`-[Person objcMethod])
    }
    (lldb) p $6.get(1)
    (method_t) $8 = {
      name = ".cxx_destruct"
      types = 0x0000000100000f80 "v16@0:8"
      imp = 0x0000000100000e40 (Example`-[Person .cxx_destruct])
    }
    (lldb) p $6.get(2)
    (method_t) $9 = {
      name = "name"
      types = 0x0000000100000f94 "@16@0:8"
      imp = 0x0000000100000de0 (Example`-[Person name])
    }
    (lldb) p $6.get(3)
    (method_t) $10 = {
      name = "setName:"
      types = 0x0000000100000f9c "v24@0:8@16"
      imp = 0x0000000100000e10 (Example`-[Person setName:])
    }
    (lldb) p $6.get(4)
    Assertion failed: (i < count), function get, file /Users/star/Desktop/objc4-781/runtime/objc-runtime-new.h, line 438.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    

    打印第5个元素时,越界了,那我们的类方法去哪了?一会再分析,我们再去看看属性

    step 5.class_rw_t提供了一个方法properties(),看起来应该是获取属性列表的

    (lldb) p $3.properties()
    (const property_array_t) $11 = {
      list_array_tt<property_t, property_list_t> = {
         = {
          list = 0x0000000100002230
          arrayAndFlag = 4294976048
        }
      }
    }
    

    返回的是property_array_t 继承于 list_array_tt,使用 list_array_tt提供的list属性,打印

    (lldb) p $11.list
    (property_list_t *const) $12 = 0x0000000100002230
    (lldb) p *$12
    (property_list_t) $13 = {
      entsize_list_tt<property_t, property_list_t, 0> = {
        entsizeAndFlags = 16
        count = 1
        first = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
      }
    }
    

    count = 1,不会吧,只有一个属性?分别打印试下

    (lldb) p $13.get(0)
    (property_t) $14 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
    (lldb) p $13.get(1)
    Assertion failed: (i < count), function get, file /Users/star/Desktop/objc4-781/runtime/objc-runtime-new.h, line 438.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    

    我们的成员变量也丢了
    step 6.再看下class_rw_t的定义,还有一个ro的方法,返回了一个class_ro_t类型的结构体,class_ro_t有一个ivars属性,看着比较像,打印一下

    (lldb) p $3.ro()
    (const class_ro_t *) $15 = 0x0000000100002138
    (lldb) p *$15
    (const class_ro_t) $16 = {
      flags = 388
      instanceStart = 8
      instanceSize = 24
      reserved = 0
      ivarLayout = 0x0000000100000f3f "\x02"
      name = 0x0000000100000f38 "Person"
      baseMethodList = 0x0000000100002180
      baseProtocols = 0x0000000000000000
      ivars = 0x00000001000021e8
      weakIvarLayout = 0x0000000000000000
      baseProperties = 0x0000000100002230
      _swiftMetadataInitializer_NEVER_USE = {}
    }
    (lldb) p $16.ivars
    (const ivar_list_t *const) $17 = 0x00000001000021e8
    (lldb) p $17.get(0)
    (ivar_t) $18 = {
      offset = 0x0000000100002250
      name = 0x0000000100000f4d "_cybl"
      type = 0x0000000100000f88 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
      Fix-it applied, fixed expression was: 
        $17->get(0)
    

    找到了 _cybl!

    • 成员变量:存储在类的bits属性中,通过bits -> data() ->ro() -> ivars获取
    • 属性,也存储在bits属性中,通过bits -> data() -> properties() -> list获取

    探索类方法存储

    猜测一下,对象的isa指向了类,类里有对象方法,类的isa指向元类,类方法会不会在元类里呢?
    step 1.获取元类地址

    (lldb) x/4gx Person.class
    0x1000022d8: 0x00000001000022b0 0x0000000100333140
    0x1000022e8: 0x0000000102118040 0x0001802400000003
    (lldb) p/x 0x00000001000022b0 & 0x0000000ffffffff8ULL
    (unsigned long long) $1 = 0x00000001000022b0
    

    step 2. 首地址偏移32字节,32字节转16进制 20

    (lldb) p (class_data_bits_t *)0x00000001000022d0
    (class_data_bits_t *) $2 = 0x00000001000022d0
    

    step 3.调用data(),获取class_rw_t* data,看下data的信息

    (lldb) p $2->data()
    (class_rw_t *) $3 = 0x0000000101053d80
    (lldb) p *$3
    (class_rw_t) $4 = {
      flags = 2684878849
      witness = 1
      ro_or_rw_ext = {
        std::__1::atomic<unsigned long> = 4294975696
      }
      firstSubclass = nil
      nextSiblingClass = 0x00007fff8c6b0c60
    }
    

    step 4.调用methods,看下输出

    (const method_array_t) $5 = {
      list_array_tt<method_t, method_list_t> = {
         = {
          list = 0x0000000100002118
          arrayAndFlag = 4294975768
        }
      }
    }
    

    返回的是method_array_t 继承于 list_array_tt,使用 list_array_tt提供的list属性,打印

    (lldb) p $5.list
    (method_list_t *const) $6 = 0x0000000100002118
    (lldb) p *$6
    (method_list_t) $7 = {
      entsize_list_tt<method_t, method_list_t, 3> = {
        entsizeAndFlags = 26
        count = 1
        first = {
          name = "classMethod"
          types = 0x0000000100000f80 "v16@0:8"
          imp = 0x0000000100000dc0 (Example`+[Person classMethod])
        }
      }
    }
    

    找到了 classMethod

    • 类的对象方法存储在bits属性中,通过bits -> methods() -> list获取方法列表
    • 类的类方法存储在元类bits属性中,通过元类bits -> methods() -> list获取方法列表

    相关文章

      网友评论

        本文标题:OC 类&类结构分析

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