上小节回顾:
*上节课我们介绍实例化对象的内存分部,其中包含了isa指针
内存和其属性
,其中isa指针中存储了非常重要的shiftCls字节码(33字节or44字节),
另外也学到结构体的
字节对齐和类对象
的字节对齐
本节知识点:
研究学习类本身的内存结构分布
-
开始:
,实例化对象objc1的内存我们知道其首isa指针存储了我们的类信息,并且我们上节内容也得到了证实
-
提出猜想:
如果我们继续往下探索,继续探索Cls的内存分部情况呢,我们会得到什么呢
-
研究实施:
我们依次往下进行四个步骤,当发现走到第三部的时候,已经开始循环了
ps:lldb输出语句
x :查看内存结构 !
x/4gx:输出4条16进制的数据
p:查看地址
p/x:查看16进制的地址
步骤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
-
step1:
首先分析源码来得到类
的内存结构 源码分析
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
-
step2:
手动操作实现
分析:
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:
取出properties
的list
内容
step6:
取出list中的第0
个位置的属性
step7:
取出list中的第1
个位置的属性
step8:
取出list中的第2
个位置的属性,程序发生崩溃
,我们发现并没有把成员变量interest
取出来,思考????
分析:
step1:
取出class_rw_t
中的methods
内容
step2:
取出methods
的list
内容
step3:
取出list中的第0
个位置的属性**
step4:
取出list中的第1
个位置的属性
.
.
.
思考:我们发现打印中并没有找到类方法 "test_ClassMethod" ????
问题解决:1.成员变量到底在哪查看呢?
我们可以objc_class -> class_data_bits_t -> class_rw_t -> ro -> ivars
中查找
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);
}
输出语句一模一样,即验证了指针平移的操作!!!
网友评论