美文网首页
OC底层4-类的探究分析

OC底层4-类的探究分析

作者: JEFF009 | 来源:发表于2021-06-18 01:08 被阅读0次

    今天通过源码的学习对类进行分析,从isa 的走位继承链来进行展开。
    我们先来看一下isa经典的流程图:

    isa流程图.png

    1 isa 的走位

    先创建一个LGPerson类然后用lldb打印验证:

    int main(int argc, const char * argv[]) {![继承链.png](https://img.haomeiwen.com/i6347155/74efcdccb400046b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
        @autoreleasepool {
            // 0x00007ffffffffff8
            LGPerson *p = [LGPerson alloc];
            NSLog(@"%@",p);
    
            lgTestClassNum();
            lgTestNSObject();
            lgTypes();
            NSLog(@"isa 我来了!");
        }
        return 0;
    }
    

    断点位置NSLog(@"%@",p);

    (lldb) x/4gx person
    0x103237ca0: 0x001d800100008365 0x0000000000000000
    0x103237cb0: 0x0000000000000000 0x0000000000000000
    (lldb) x person
    0x103237ca0: 65 83 00 00 01 80 1d 00 00 00 00 00 00 00 00 00  e...............
    0x103237cb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    (lldb) p/x person
    (LGPerson *) $2 = 0x0000000103237ca0
    (lldb) x/4gx 0x0000000103237ca0
    0x103237ca0: 0x001d800100008365 0x0000000000000000
    0x103237cb0: 0x0000000000000000 0x0000000000000000
    (lldb) 0x001d800100008365 通过几种方法可以拿到我们对象的 isa
    error: '0x001d800100008365' is not a valid command.
    (lldb) p/x 0x001d800100008365 & 0x00007ffffffffff8
    (long) $3 = 0x0000000100008360
    (lldb) p 0x0000000100008360
    (long) $4 = 4295000928
    (lldb) po 0x0000000100008360
    LGPerson
    
    (lldb) x/4gx 0x0000000100008360
    0x100008360: 0x0000000100008338 0x00007fff8e7a2118
    0x100008370: 0x00007fff67082140 0x0000802c00000000
    (lldb) po 0x0000000100008338 & 0x00007ffffffffff8
    LGPerson
    
    (lldb) 竟然也得到LGPerson,表示不可思议
    error: '竟然也得到LGPerson,表示不可思议' is not a valid command.
    (lldb) p/x 0x0000000100008338 & 0x00007ffffffffff8
    (long) $7 = 0x0000000100008338
    (lldb) 
    
    

    通过lldb打印我们发现竟然可以获取到两个LGPerson
    此时猜想0x0000000100008360 VS 0x0000000100008338
    哪一个才是我们开辟的类?

    分析类对象内存存在个数

    void lgTestClassNum(void){
        Class class1 = [LGPerson class];
        Class class2 = [LGPerson alloc].class;
        Class class3 = object_getClass([LGPerson alloc]);
        Class class4 = [LGPerson alloc].class;
        NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
    }
    
    

    断点位位置 lgTestClassNum();

    2021-06-17 22:59:44.232703+0800 002-isa分析[75019:5584135] 
    0x100008360-
    0x100008360-
    0x100008360-
    0x100008360
    

    打印结构发现:0x0000000100008360 才是我们的类,0x0000000100008338不是我们的类,那是是什么?一个新的东西,元类?

    我可以得到暂时的结论:对象 isa -> 类 isa -> 元类
    接下来我们通过MachOView来分析下,新的东西是什么:

    截屏2021-06-17 下午11.06.50.png
    LGPerson.png
    截屏2021-06-17 下午11.15.26.png

    可以看到新的东西多了个:
    OBJC_METACLASS$_LGPerson ,也就我们平常听说的元类,由系统自动生成,证明我们的结论是正确的对象 isa -> 类 isa -> 元类

    重新运行项目,我们继续探索:

    (lldb) x/4gx person
    0x103265100: 0x001d800100008365 0x0000000000000000
    0x103265110: 0x0000000000000000 0x0000000000000000
    (lldb) x person
    0x103265100: 65 83 00 00 01 80 1d 00 00 00 00 00 00 00 00 00  e...............
    0x103265110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    (lldb)  p/x person
    (LGPerson *) $2 = 0x0000000103265100
    (lldb) x/4gx 0x0000000103265100
    0x103265100: 0x001d800100008365 0x0000000000000000
    0x103265110: 0x0000000000000000 0x0000000000000000
    (lldb) p/x 0x001d800100008365 & 0x00007ffffffffff8
    (long) $3 = 0x0000000100008360
    (lldb) p 0x0000000100008360
    (long) $4 = 4295000928
    (lldb) po 0x0000000100008360
    LGPerson
    
    (lldb) x/4gx 0x0000000100008360
    0x100008360: 0x0000000100008338 0x00007fff8e7a2118
    0x100008370: 0x00007fff67082140 0x0000802c00000000
    (lldb) po 0x0000000100008338 & 0x00007ffffffffff8
    LGPerson
    
    (lldb)  p/x 0x0000000100008338 & 0x00007ffffffffff8
    (long) $7 = 0x0000000100008338
    (lldb) x/4gx 0x0000000100008338
    0x100008338: 0x00007fff8e7a20f0 0x00007fff8e7a20f0
    0x100008348: 0x0000000100405cc0 0x0003e03500000007
    (lldb)  po 0x00007fff8e7a20f0 & 0x00007ffffffffff8
    NSObject
    
    (lldb) po NSObject.class
    NSObject
    
    (lldb) p/x NSObject.class
    (Class) $10 = 0x00007fff8e7a2118 NSObject
    2021-06-17 23:40:25.795492+0800 002-isa分析[76268:5634302] <LGPerson: 0x103265100>
    (lldb) x/4gx 0x00007fff8e7a2118
    0x7fff8e7a2118: 0x00007fff8e7a20f0 0x0000000000000000
    0x7fff8e7a2128: 0x00000001032654a0 0x0002801000000003
    (lldb) p/x 0x00007fff8e7a20f0 & 0x00007ffffffffff8
    (long) $11 = 0x00007fff8e7a20f0
    (lldb) po 0x00007fff8e7a20f0 & 0x00007ffffffffff8
    NSObject
    
    (lldb) 
    
    截屏2021-06-17 下午11.48.14.png

    由上图代码可以总结 isa 的走位大致如下:

    isa走向.png

    1.对象 isa -> 类 isa -> 元类 isa -> 根元类 isa -> 根元类
    2.根类 isa -> 根元类 isa

    2 继承链

    2.1.继承链
    void lgTestSuperClass(void){
        LGTeacher *t = [LGTeacher alloc];
        LGPerson  *p = [LGPerson alloc];
        NSLog(@"%@-%@",t,p);
        
        NSLog(@"%@",class_getSuperclass(LGTeacher.class));
        NSLog(@"%@",class_getSuperclass(LGPerson.class));
        NSLog(@"%@",class_getSuperclass(NSObject.class));
    }
    
    2021-06-18 00:49:01.936188+0800 002-isa分析[78024:5697290] <LGTeacher: 0x103107950>-<LGPerson: 0x103107970>
    2021-06-18 00:49:01.936282+0800 002-isa分析[78024:5697290] LGPerson
    2021-06-18 00:49:01.936325+0800 002-isa分析[78024:5697290] NSObject
    2021-06-18 00:49:09.086433+0800 002-isa分析[78024:5697290] (null)
    

    类的继承总结:
    继承于 父类
    父类 继承于 根类
    根类 继承于 nil

    2.1.NSObject 元类链
    void lgTestNSObject(void){
        // NSObject实例对象
        NSObject *object1 = [NSObject alloc];
        // NSObject类
        Class class = object_getClass(object1);
        // NSObject元类
        Class metaClass = object_getClass(class);
        // NSObject根元类
        Class rootMetaClass = object_getClass(metaClass);
        // NSObject根根元类
        Class rootRootMetaClass = object_getClass(rootMetaClass);
        NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类",object1,class,metaClass,rootMetaClass,rootRootMetaClass);
        
        // LGPerson元类
        Class pMetaClass = object_getClass(LGPerson.class);
        Class psuperClass = class_getSuperclass(pMetaClass);
        NSLog(@"%@ - %p",psuperClass,psuperClass);
        
        // LGTeacher -> LGPerson -> NSObject
        // 元类也有一条继承链
        Class tMetaClass = object_getClass(LGTeacher.class);
        Class tsuperClass = class_getSuperclass(tMetaClass);
        NSLog(@"%@ - %p",tsuperClass,tsuperClass);
        
        // NSObject 根类特殊情况
        Class nsuperClass = class_getSuperclass(NSObject.class);
        NSLog(@"%@ - %p",nsuperClass,nsuperClass);
        // 根元类 -> NSObject
        Class rnsuperClass = class_getSuperclass(metaClass);
        NSLog(@"%@ - %p",rnsuperClass,rnsuperClass);
    }
    
    2021-06-18 00:39:02.147072+0800 002-isa分析[76268:5634302] 
    0x1006040e0 实例对象
    0x7fff8e7a2118 类
    0x7fff8e7a20f0 元类
    0x7fff8e7a20f0 根元类
    0x7fff8e7a20f0 根根元类
    2021-06-18 00:39:21.070988+0800 002-isa分析[76268:5634302] NSObject - 0x7fff8e7a20f0
    2021-06-18 00:39:21.071134+0800 002-isa分析[76268:5634302] LGPerson - 0x100008338
    2021-06-18 00:39:21.071260+0800 002-isa分析[76268:5634302] (null) - 0x0
    2021-06-18 00:39:21.071362+0800 002-isa分析[76268:5634302] NSObject - 0x7fff8e7a2118
    

    元类的继承总结:
    元类 继承于 父元类
    父元类 继承于 根元类
    根元类 继承于 根类
    根类 继承于 nil

    继承链.png
    通过上面初步从 isa 的走位和继承类两个地方入手对类初步的探究与分析;

    3.源码分析类的结构

    接下来我们继续来分析类的结构 查看Class 源码

    typedef struct objc_class *Class;
    
    
    struct objc_class : objc_object {
    ...
        // Class ISA;
        Class superclass;
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    ...
    }
    

    我们在源码中搜索objc_class,发现objc_class继承于objc_object,而objc_object里面包含一个默认的isa,
    继续查看bits是存储类的信息的,而bits是有class_data_bits_t定义的,那我们就看下class_data_bits_t的源码:

    struct class_data_bits_t {
        friend objc_class;
    
        // Values are the FAST_ flags above.
        uintptr_t bits;
    private:
    .... 省略代码.... 
    public:
    
        class_rw_t* data() const {
            return (class_rw_t *)(bits & FAST_DATA_MASK);
        }
        .... 省略代码.... 
        // Get the class's ro data, even in the presence of concurrent realization.
        // fixme this isn't really safe without a compiler barrier at least
        // and probably a memory barrier when realizeClass changes the data field
        const class_ro_t *safe_ro() const {
            class_rw_t *maybe_rw = data();
            if (maybe_rw->flags & RW_REALIZED) {
                // maybe_rw is rw
                return maybe_rw->ro();
            } else {
                // maybe_rw is actually ro
                return (class_ro_t *)maybe_rw;
            }
        }
    .... 省略代码.... 
    };
    

    主要看下class_rw_tclass_ro_t

    struct class_rw_t {
    .... 省略代码.... 
        const method_array_t methods() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
            } else {
                return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
            }
        }
    
        const property_array_t properties() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
            } else {
                return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
            }
        }
    
        const protocol_array_t protocols() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
            } else {
                return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
            }
        }
    };
    

    继续通过lldb来打印bits里面的信息

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface LGPerson : NSObject{
        NSString *subject;
    }
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) NSString *hobby;
    
    - (void)sayNB;
    + (void)say666;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    
    #import "LGPerson.h"
    
    @implementation LGPerson
    
    - (instancetype)init{
        if (self = [super init]) {
            self.name = @"Cooci";
        }
        return self;
    }
    
    - (void)sayNB{
        
    }
    + (void)say666{
        
    }
    
    @end
    

    打印结构:

    KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
    (lldb) p/x LGPerson.class
    (Class) $0 = 0x0000000100004410 LGPerson
    (lldb) p (class_data_bits_t*) 0x0000000100004430
    (class_data_bits_t *) $1 = 0x0000000100004430
    (lldb) p $1->data()
    (class_rw_t *) $2 = 0x0000000101e060f0
    (lldb) p *$2
    (class_rw_t) $3 = {
      flags = 2148007936
      witness = 0
      ro_or_rw_ext = {
        std::__1::atomic<unsigned long> = {
          Value = 4294984104
        }
      }
      firstSubclass = nil
      nextSiblingClass = NSUUID
    }
    (lldb) p $3.methods()
    (const method_array_t) $4 = {
      list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
         = {
          list = {
            ptr = 0x00000001000041f0
          }
          arrayAndFlag = 4294984176
        }
      }
    }
    (lldb) p $4.list
    (const method_list_t_authed_ptr<method_list_t>) $5 = {
      ptr = 0x00000001000041f0
    }
    (lldb) p $5.ptr
    (method_list_t *const) $6 = 0x00000001000041f0
    (lldb) p *$6
    (method_list_t) $7 = {
      entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)
    }
    (lldb) p $7.get(0)
    (method_t) $8 = {}
    (lldb)  p $7.get(1)
    (method_t) $9 = {}
    (lldb)  p $7.get(2)
    (method_t) $10 = {}
    (lldb)  p $7.get(3)
    (method_t) $11 = {}
    (lldb) p $7.get(4)
    (method_t) $12 = {}
    (lldb) p $7.get(5)
    (method_t) $13 = {}
    (lldb) p $7.get(6)
    Assertion failed: (i < count), function get, file /Users/fengjiefeng/Desktop/逻辑教育V14--iOS底层开发课程/1.iOS底层大师班/20210616-大师班-第4节课-类的原理分析上/20210616-大师班第4天-类的原理分析上/01--课堂代码/004-类的结构分析/runtime/objc-runtime-new.h, line 624.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    (lldb) p $7.get(0).big()
    (method_t::big) $20 = {
      name = "sayNB"
      types = 0x0000000100003f77 "v16@0:8"
      imp = 0x0000000100003d40 (KCObjcBuild`-[LGPerson sayNB])
    }
    (lldb)  p $7.get(1).big()
    (method_t::big) $21 = {
      name = "hobby"
      types = 0x0000000100003f6f "@16@0:8"
      imp = 0x0000000100003db0 (KCObjcBuild`-[LGPerson hobby])
    }
    (lldb)  p $7.get(2).big()
    (method_t::big) $22 = {
      name = "setHobby:"
      types = 0x0000000100003f8b "v24@0:8@16"
      imp = 0x0000000100003de0 (KCObjcBuild`-[LGPerson setHobby:])
    }
    (lldb)  p $7.get(3).big()
    (method_t::big) $23 = {
      name = "init"
      types = 0x0000000100003f6f "@16@0:8"
      imp = 0x0000000100003ce0 (KCObjcBuild`-[LGPerson init])
    }
    (lldb)  p $7.get(4).big()
    (method_t::big) $24 = {
      name = "name"
      types = 0x0000000100003f6f "@16@0:8"
      imp = 0x0000000100003d50 (KCObjcBuild`-[LGPerson name])
    }
    (lldb) p $7.get(5).big()
    (method_t::big) $25 = {
      name = "setName:"
      types = 0x0000000100003f8b "v24@0:8@16"
      imp = 0x0000000100003d80 (KCObjcBuild`-[LGPerson setName:])
    }
    
    (lldb) p $3.properties()
    (const property_array_t) $14 = {
      list_array_tt<property_t, property_list_t, RawPtr> = {
         = {
          list = {
            ptr = 0x00000001000042f0
          }
          arrayAndFlag = 4294984432
        }
      }
    }
    (lldb) p $14.list
    (const RawPtr<property_list_t>) $15 = {
      ptr = 0x00000001000042f0
    }
    (lldb) p $15.ptr
    (property_list_t *const) $16 = 0x00000001000042f0
    (lldb) p *16
    error: <user expression 18>:1:1: indirection requires pointer operand ('int' invalid)
    *16
    ^~~
    (lldb) p *$16
    (property_list_t) $17 = {
      entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
    }
    (lldb) p $17.get(0)
    (property_t) $18 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
    (lldb) p $17.get(1)
    (property_t) $19 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")
    (lldb) p $17.get(2)//越界了
    
    Assertion failed: (i < count), function get, file /Users/fengjiefeng/Desktop/逻辑教育V14--iOS底层开发课程/1.iOS底层大师班/20210616-大师班-第4节课-类的原理分析上/20210616-大师班第4天-类的原理分析上/01--课堂代码/004-类的结构分析/runtime/objc-runtime-new.h, line 624.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    (lldb) 
    

    还未完成....

    相关文章

      网友评论

          本文标题:OC底层4-类的探究分析

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