前言
前面一个章节我们已经分析出来对象的首地址是isa 以及分析了isa分为纯的isa以及NONPOINTER_ISA 分析了NONPOINTER_ISA的结构以及用两种方式还原类的过程,但类ISA我们还有待进一步探索。
引入元类的由来
我们先来看下类的源码结构
类也会有个隐藏参数ISA
struct objc_object {
Class **_Nonnull** isa OBJC_ISA_AVAILABILITY;
};
来自继承的父类objc_objct
思考:那类的ISA&ISA_MASK它又是什么了?
代码分析
LGPerson *p = [LGPerson alloc];
NSLog(@"%@",p);
这里就引出了元类的概念 他为什么是元类了?
MachOview 分析看元类
这里可以看到 元类是真真实实在苹果底层中存在的
元类是系统编译器自动创建的,和用户没关系- 对象的
isa指向类,类对象的isa指向元类 - 类的
类名和它关联类即元类的类名是一样的(只有关联元类才有类名)
isa走位图
分析普通对象
分析系统类
NSObject * p =[[NSObject alloc]init];
NSLog(@"%@",p);
.class 与 object_getClass 与 class_getSuperclass 补充
+ (Class)class {
return self;
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
Class class_getSuperclass(Class cls)
{
if (!cls) return nil;
return cls->getSuperclass();
}
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));
}
#pragma mark - 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);
}
官方isa走位图
内存偏移
如果要想获取对象内存中的变量,底层实现方式是对象的首地址+偏移值。下面探究下内存偏移
普通指针
int main(int argc, char * argv[]) {
@autoreleasepool {
int a = 10;
int b = 10;
int * p = &a;
int * q = &b;
NSLog(@"---%d----%p---%p",a,p,&p);
NSLog(@"---%d----%p---%p",b,q,&q);
}
return 0;
}
内存偏移[11954:347138] ---10----0x7ffedfe69c8c---0x7ffedfe69c80
内存偏移[11954:347138] ---10----0x7ffedfe69c88---0x7ffedfe69c78
a和b的值都是10,但是a和b的地址不一样,这就是常说的深拷贝a的地址是0x7ffedfe69c8c,b的地址是0x7ffedfe69c88,相差4字节,主要取决于a的类型a>b>p>q的地址,且它们全部在栈区。
对象指针
int main(int argc, char * argv[]) {
@autoreleasepool {
LWPerson * p1 = [LWPerson alloc];
LWPerson * p2 = [LWPerson alloc];
NSLog(@"---%@----%p",p1,&p1);
NSLog(@"---%@----%p",p2,&p2);
}
return 0;
}
内存偏移[12536:388334] ---<LWPerson: 0x600002000000>----0x7ffee357bc70
内存偏移[12536:388334] ---<LWPerson: 0x600002000010>----0x7ffee357bc68
alloc开辟的内存在堆区,局部变量开辟的内存在栈区- 堆区
低地址 -->高地址,栈区高地址 -->低地址
数组指针
int main(int argc, char * argv[]) {
@autoreleasepool {
int c[4] = {1,2,3,4};
int *d = c;
NSLog(@"%p - %p - %p ",&c,&c[0],&c[1]);
NSLog(@"%p - %p - %p ",d,d+1,d+2);
for (int i = 0; i<4; i++) {
//(d+i) 取地址里面的值
int value = *(d+i);
NSLog(@"value = %d",value);
}
}
return
}
2021-06-17 16:32:13.132035+0800 内存偏移[12919:415236] 0x7ffeebfd9c80 - 0x7ffeebfd9c80 - 0x7ffeebfd9c84
2021-06-17 16:32:13.132122+0800 内存偏移[12919:415236] 0x7ffeebfd9c80 - 0x7ffeebfd9c84 - 0x7ffeebfd9c88
2021-06-17 16:32:13.132206+0800 内存偏移[12919:415236] value = 1
2021-06-17 16:32:13.132287+0800 内存偏移[12919:415236] value = 2
内存偏移[12919:415236] value = 3
内存偏移[12919:415236] value = 4
-
数组的地址就是数组元素中的
首地址,即&c和&c[0]都是首地址 -
数组中每个元素之间的
地址间隔,根据当前元素的数据类型决定的 -
数组的元素地址可以通过
首地址+n*类型大小方式,这种方式是数组中的元素类型必须相同。 -
数组元素不相同用
首地址+偏移量方式,根据当前变量的偏移值(需要前面类型大小相加) -
内存地址就是内存元素的
首地址 -
内存偏移可以根据
首地址+偏移值方法获取相对应变量的地址