Isa走位图分析与类的原理(上)

998 阅读4分钟

前言

前面一个章节我们已经分析出来对象的首地址是isa 以及分析了isa分为纯的isa以及NONPOINTER_ISA 分析了NONPOINTER_ISA的结构以及用两种方式还原类的过程,但类ISA我们还有待进一步探索

引入元类的由来

我们先来看下类的源码结构

1.jpg

类也会有个隐藏参数ISA

struct objc_object {

    Class **_Nonnull** isa  OBJC_ISA_AVAILABILITY;

};

来自继承的父类objc_objct

思考:那类的ISA&ISA_MASK它又是什么了?

代码分析

LGPerson *p = [LGPerson alloc];

 NSLog(@"%@",p);

2.jpg

这里就引出了元类的概念 他为什么是元类了?

MachOview 分析看元类

3.jpg

4.jpg

这里可以看到 元类是真真实实在苹果底层中存在的

  • 元类是系统编译器自动创建的,和用户没关系
  • 对象的isa指向,类对象的isa指向元类
  • 类的类名和它关联类即元类类名是一样的(只有关联元类才有类名)

isa走位图

分析普通对象

6.png

分析系统类

NSObject * p =[[NSObject alloc]init];

NSLog(@"%@",p);

7.jpg

8.png

.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();

}

12.png

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);

}

10.jpg

13.png

15.png

官方isa走位图

isa流程图.png

内存偏移

如果要想获取对象内存中的变量,底层实现方式是对象的首地址+偏移值。下面探究下内存偏移

普通指针

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
  • ab的值都是10,但是ab的地址不一样,这就是常说的深拷贝
  • a的地址是0x7ffedfe69c8cb的地址是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*类型大小方式,这种方式是数组中的元素类型必须相同。

  • 数组元素不相同用首地址+偏移量方式,根据当前变量的偏移值(需要前面类型大小相加)

  • 内存地址就是内存元素的首地址

  • 内存偏移可以根据首地址偏移值方法获取相对应变量的地址

类的底层结构

16.jpg