程序员

二:类的内存结构分析

2020-11-11  本文已影响0人  Mr姜饼

上小节回顾:
*上节课我们介绍实例化对象的内存分部,其中包含了isa指针内存和其属性,其中isa指针中存储了非常重要的shiftCls字节码(33字节or44字节),另外也学到结构体的字节对齐和类对象的字节对齐

本节知识点:
研究学习类本身的内存结构分布


,实例化对象objc1的内存我们知道其首isa指针存储了我们的类信息,并且我们上节内容也得到了证实

如果我们继续往下探索,继续探索Cls的内存分部情况呢,我们会得到什么呢

ps:lldb输出语句
x :查看内存结构 !
x/4gx:输出4条16进制的数据
p:查看地址
p/x:查看16进制的地址

image.png

步骤1:
我们得到类对象的isa内存,并且与面具MASK进行与算,得到 shiftCls信息,并且打印得到我们的LGPerson
步骤2:
接着我们拿到LGPerson类本身进行内存结构查看,同样的我们通过isa内存与面具MASK进行“与”运算,拿到结果我们输出打印,得到的还是LGPerson,而此时的地址却与第一步得到的不同,那么此isa指针指向的又是什么东西呢?
步骤3:
我们拿到上一步得到的LGPerson类本身进行内存结构查看,同样的我们通过isa内存与面具MASK进行“与”运算,拿到结果我们输出打印,得到的是NSObject,那么此isa指针指向的又是什么东西呢?
步骤4:
我们拿到上一步得到的NSObject类本身进行内存结构查看,同样的我们通过isa内存与面具MASK进行“与”运算,拿到结果我们输出打印,得到的依然是NSObject,而此时的地址却与上一步得到的一模一样,此时isa指针地址指向的又是什么呢。

isa的指针流程图:
实例化对象(objc1) ->1. 类(Person)- > 2.元类(Person)-> 3.根元类(NSObject) ->4.根元类(NSObject)


属性&成员变量&实例变量


@interface LGPerson : NSObject
{
    NSString* interst;
    
}
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, strong) NSString *name;


//实例化方法
- (void)test_InstanceMethod;
//类方法
+ (void)test_ClassMethod;
@end

属性&成员变量&实例变量的区别:

属性 = 带下划线成员变量 + setter + getter ⽅法

实例变量 : 特殊的成员变量 (类的实例化)


小节课题:通过lldb来获取类的属性和方法:


@interface LGPerson : NSObject
{
    NSString* interst;
    
}
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, strong) NSString *name;


//实例化方法
- (void)test_InstanceMethod;
//类方法
+ (void)test_ClassMethod;
@end

objc_object:

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

objc_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

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    void setInfo(uint32_t set) {
        ASSERT(isFuture()  ||  isRealized());
        data()->setFlags(set);
    }
    .
    .//此处省略(以上才是重点)
    .
}

:

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;

    .
    .//此处省略
    .

    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>()->ro;
        }
        return v.get<const class_ro_t *>();
    }

    void set_ro(const class_ro_t *ro) {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            v.get<class_rw_ext_t *>()->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }

    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 *>()->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>()->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 *>()->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>()->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 *>()->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
        }
    }
};

初步小结:
取属性objc_class -> class_data_bits_t -> class_rw_t -> properties
取方法objc_class -> class_data_bits_t -> class_rw_t -> methods

image.png image.png

分析:
step1:拿到LGPerson的类内存分部,即objc_class的内存,
step2:内存地址指针平移“32”字节0x0000000100002258>0x0000000100002278,即取到了class_data_bits_t的字节内存,注意这里为什么是平移32字节,其原因是因为 Class ISA + Class superclass + cache_t cache 为32字节,再利用指针平移的思路,即能拿到 class_data_bits_t的首地址指针 **
step3:
取出class_rw_t内容
step4:取出properties内容
step5:
取出propertieslist内容
step6:取出list中的第0个位置的属性
step7:取出list中的第1个位置的属性
step8:取出list中的第2个位置的属性,程序发生崩溃,我们发现并没有把成员变量interest取出来,思考????

image.png

分析:

step1:取出class_rw_t中的methods内容
step2:取出methodslist内容
step3:
取出list中的第0个位置的属性**
step4:取出list中的第1个位置的属性
.
.
.
思考:我们发现打印中并没有找到类方法 "test_ClassMethod" ????


问题解决:1.成员变量到底在哪查看呢?

我们可以objc_class -> class_data_bits_t -> class_rw_t -> ro -> ivars中查找

image.png
image.png

总结:
在 "ivars" 中,我们既可以找到成员变量 也可以找到 属性 成员


问题解决:2.类方法到底在哪查看呢?

猜想:
是不是类方法根本就不存在 类 内存中呀

辅助工具:
MachOView: 查看编译内存中是否存在类方法

image.png

事实证明:类方法确实存在
好了 不兜圈子了,那我们如何找到呢😺

step1:取到LGPerson 的 元类
.....
step7:类方法已经获取到

总结:对象方法存在类里面,类方法存在元类里面

猜想成功、大功告成✿✿ヽ(°▽°)ノ✿


课外扩展:

指针平移:

    int c[4] = {1,2,3,4};
    int* x = c ;
    for (int i = 0; i < 4 ; i++) {
        int d = c[i];
        //等价于
        int b = *(x+i);
        NSLog(@"%d",d);
        NSLog(@"%d",b);
    }

输出语句一模一样,即验证了指针平移的操作!!!

上一篇 下一篇

猜你喜欢

热点阅读