OC底层原理05- 类的原理分析下

180 阅读2分钟

前言

类实例话的属性变量 以及实例方法 以及协议的类方法 实例方法 属性 通过上一篇已经已经找到了 如何找到类方法和成员变量 我们这节继续搞一搞

一.如何找到成员变量

Xnip2021-06-22_20-54-16.jpg

(lldb) p/x LGPerson.class  //打印LGPerson类地址
(Class) $0 = 0x00000001000088b0 LGPerson
(lldb) x/4gx 0x00000001000088b0 //打印内存地址
0x1000088b0: 0x00000001000088d8 0x000000010036a140
0x1000088c0: 0x00000001012296a0 0x0002804000000003
(lldb) p/x (class_data_bits_t *) 0x1000088d0 //通过0x1000088b0平移32位得到0x1000088d0 获取 class_data_bits_t
(class_data_bits_t *) $1 = 0x00000001000088d0
(lldb) p $1->data() //获取 class_rw_t
(class_rw_t *) $2 = 0x0000000101229640
(lldb) p $2->ro() //获取 class_ro_t
(const class_ro_t *) $3 = 0x00000001000081a8
(lldb) p $3-> ivars//获取ivar_list_t
(const ivar_list_t *const) $4 = 0x00000001000082d0
(lldb)p *$4 //打印$4内容
(const ivar_list_t) $5 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 6)
}

Xnip2021-06-22_21-06-57.jpg

//打印成员变量
(lldb) p $5.get(0)
(ivar_t) $6 = {
  offset = 0x00000001000087e0
  name = 0x0000000100003d28 "subject"
  type = 0x0000000100003e7a "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $5.get(1)
(ivar_t) $7 = {
  offset = 0x00000001000087e8
  name = 0x0000000100003d30 "array"
  type = 0x0000000100003e86 "@\"NSArray\""
  alignment_raw = 3
  size = 8
}
(lldb) p $5.get(2)
(ivar_t) $8 = {
  offset = 0x00000001000087f0
  name = 0x0000000100003d36 "dic"
  type = 0x0000000100003e91 "@\"NSDictionary\""
  alignment_raw = 3
  size = 8
}
(lldb) p $5.get(3)
(ivar_t) $9 = {
  offset = 0x00000001000087f8
  name = 0x0000000100003d3a "_age"
  type = 0x0000000100003ea1 "i"
  alignment_raw = 2
  size = 4
}
(lldb) p $5.get(4)
(ivar_t) $10 = {
  offset = 0x0000000100008800
  name = 0x0000000100003d3f "_name"
  type = 0x0000000100003e7a "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $5.get(5)
(ivar_t) $11 = {
  offset = 0x0000000100008808
  name = 0x0000000100003d45 "_hobby"
  type = 0x0000000100003e7a "@\"NSString\""
  alignment_raw = 3
  size = 8
}

如上所示 我们LLDB打印的成员变量里面包含属性 下面我们探究一下为什么

二.成员变量和属性

Xnip2021-06-22_22-18-36.jpg 1.进入终端 执行 xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc LGPerson.m -o LGPerson.cpp

Xnip2021-06-22_22-27-52.jpg

2.objc_setProperty内存平移有什么区别 Xnip2021-06-22_22-44-18.jpg setName setAge -> objc_setProperty(中间封装) ->LLVM底层代码 我们查看一下LLVM源码 查找objc_setProperty

Xnip2021-06-22_23-20-03.jpg

Xnip2021-06-22_23-21-25.jpg

Xnip2021-06-22_23-23-49.jpg

Xnip2021-06-22_23-25-12.jpg

Xnip2021-06-22_23-12-37.jpg 通过LLVM分析 copy会执行objc_setProperty 下面用个例子

Xnip2021-06-22_23-28-51.jpg 得出结果

Xnip2021-06-22_23-30-51.jpg 都是copy 为什么有的有objc_getProperty 有的是内存平移呢 是否根atomic有关系呢

Xnip2021-06-23_00-43-09.jpg

Xnip2021-06-23_00-43-25.jpg Xnip2021-06-23_00-44-03.jpg 必须是atomic copy 才会创建objc_getProperty

3.编码 Xnip2021-06-22_22-46-28.jpg

例子: @16@0:8

@ id类型

16 占用的内存

@ id类型(函数的隐士参数self)

0 从0号位置开始

: SEL

8 从8号位置开始

对应的这些编码苹果都有,地址是(developer.apple.com/library/arc…)

三.如何找到类方法

1.为什么要有元类 存储类方法

Xnip2021-06-22_21-55-21.jpg

(lldb) p/x object_getClass(LGPerson.class) //获取元类地址
(Class) $0 = 0x00000001000088d8
(lldb) p (class_data_bits_t *) 0x00000001000088f8 //平移动32位 获取class_data_bits_t
(class_data_bits_t *) $1 = 0x00000001000088f8
(lldb) p $1->data() //获取class_rw_t
(class_rw_t *) $2 = 0x0000000101e125d0
(lldb) p $2.methods()//获取methods
(const method_array_t) $3 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008798
      }
      arrayAndFlag = 4295002008
    }
  }
}
  Fix-it applied, fixed expression was: 
    $2->methods()
(lldb) p $3.list.ptr
(method_list_t *const) $4 = 0x0000000100008798
(lldb) p *$4
(method_list_t) $5 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}
(lldb)  p $5.get(0).big()
(method_t::big) $7 = {
  name = "say666"
  types = 0x0000000100003e72 "v16@0:8"
  imp = 0x0000000100003bc0 (KCObjcBuild`+[LGPerson say666])
}

结论:对象方法在类中 类方法在元类中

Xnip2021-06-23_00-05-01.jpg 1.第一种验证方式

//获取LGPerson类中所有的方法
void lgObjc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //获取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        LGLog(@"Method, name: %@", key);
    }
    free(methods);
}

    创建对象调用上面方法
    LGPerson *person = [LGPerson alloc];
    Class pClass     = object_getClass(person);//LGPerson类
    lgObjc_copyMethodList(pClass);

    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);//LGPerson元类
    NSLog(@"*************");
    lgObjc_copyMethodList(metaClass);
    
    打印显示结果 表示上面的结论正确
    
    Method, name: sayHello
    Method, name: name
    Method, name: .cxx_destruct
    Method, name: setName:
    Method, name: obj
    Method, name: setObj:
    *************
    Method, name: sayHappy

2.第二种验证方式

void lgInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);//元类
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));//验证类里面是否有sayHello 有的
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));//验证元类里面是否有sayHello 无

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));//验证类里面是否有sayHappy 无
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));//验证元类里面是否有sayHappy 有
    
    LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
调用
LGPerson *person = [LGPerson alloc];
Class pClass     = object_getClass(person);//LGPerson类
lgInstanceMethod_classToMetaclass(pClass);
打印得出结果
lgInstanceMethod_classToMetaclass - 0x1000081c0-0x0-0x0-0x100008158

3.第三种验证方式

void lgClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

//    - (void)sayHello;
//    + (void)sayHappy;
    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

调用
LGPerson *person = [LGPerson alloc];
Class pClass     = object_getClass(person);//LGPerson类lgClassMethod_classToMetaclass(pClass);
打印得出结果
lgClassMethod_classToMetaclass-0x0-0x0-0x100008158-0x100008158

为什么元类类面有sayHappy方法 Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));//验证元类里面是否有sayHappy

Xnip2021-06-23_00-08-56.jpg

Xnip2021-06-23_00-09-43.jpg

Xnip2021-06-23_00-10-02.jpg 元类-》class_getInstanceMethod对象方法sayHappy 在底层没有类方法 所有的都是对象方法

4.第四种验证方式

void lgIMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));// 0
    // sel -> imp 方法的查找流程 imp_farw
    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); // 0
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);
    
    
     调用 
    LGPerson *person = [LGPerson alloc];
    Class pClass     = object_getClass(person);//LGPerson类
    lgIMP_classToMetaclass(pClass);
    打印结果
    0x100003ad0-0x7fff2046bac0-0x7fff2046bac0-0x100003b10
    
}

补充

p *($6+1)等同于p * $7 内存偏移

struct class_data_bits_t { friend objc_class } friend:objc里面的私有方法都可以使用

对象的isa&ISA_MASK得到类的isa (对象isa 和 类不一样)

类的isa &ISA_MASK得到是元类的isa (类isa 和 元类一样)