OC--底层原理之类结构的分析

今天我们经过源码的探索来分析下类的结构,我们从isa类的继承两方面来分析:
我们先看一个经典的走位图:isa流程图
我们来验证下isa的走位:
首先我们先创建一个LGPerson类然后用lldb打印一验证:

@interface LGPerson : NSObject
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {

       LGPerson *person = [LGPerson alloc];
       NSLog(@"%@",person); 
    return 0;
}
  • (lldb) x/4gx person : 打印对象的内存
    0x10056bcb0: 0x001d800100008275 0x0000000000000000
    0x10056bcc0: 0x0000000000000000 0x0000000000000000
    0x001d800100008275 即为对象的isa
  • (lldb) p/x 0x001d800100008275 & 0x00007ffffffffff8ULL:打印isa的指针
    (unsigned long long) $1 = 0x0000000100008270
  • (lldb) po 0x0000000100008270 : 打印isa 指针
    LGPerson
    这时候得到了LGPerson这个类,说明isa 指向 类
  • (lldb) x/4gx 0x0000000100008270: 接着打印LGPerson这个类的内存
    0x100008270: 0x0000000100008248 0x00007fff92b5e118
    0x100008280: 0x00007fff6b38d140 0x0000802c00000000
    得到类的isa0x0000000100008248,然后我们获取这个isa的指针
  • (lldb) p/x 0x0000000100008248 & 0x00007ffffffffff8ULL: 打印isa的指针
    (unsigned long long) $3 = 0x0000000100008248
  • (lldb) po 0x0000000100008248:打印这个指针
    LGPerson
    这是我们发现又得到一个LGPerson,此时这个类就是元类
    接着我们在去打印这个元类的内存
  • (lldb) x/4gx 0x0000000100008248:
    0x100008248: 0x00007fff92b5e0f0 0x00007fff92b5e0f0
    0x100008258: 0x0000000100404760 0x0003e03500000007
    此时我们获取到元类isa,然后我们获取元类 isa的指针
  • (lldb) p/x 0x00007fff92b5e0f0 & 0x00007ffffffffff8ULL:
    (unsigned long long) $5 = 0x00007fff92b5e0f0
    得到了元类 isa的指针,然后我们去打印这个指针
  • (lldb) po 0x00007fff92b5e0f0:
    NSObject
    我们得到了一个NSObject,那此时的这个NSObject,到底是不是根类 NSObject呢?我们验证一下
    -(lldb) p/x NSObject.class : 我们打印下根类的地址指针
    (Class) $8 = 0x00007fff92b5e118 NSObject
    我们发现0x00007fff92b5e118这个指针不等于上面我们获取到的0x00007fff92b5e0f0这个指针,说明上面我们获取到的NSObject不是根类,我们给这个NSObject起了个名字叫根元类
  • (lldb) x/4gx 0x00007fff92b5e0f0: 继续打印根元类的内存信息获取isa
    0x7fff92b5e0f0: 0x00007fff92b5e0f0 0x00007fff92b5e118
    0x7fff92b5e100: 0x0000000100607300 0x0005e03100000007
    此时我们发现isa 0x00007fff92b5e0f0根元类的指针地址是一样的,这时我们在打印下这个isa
  • (lldb) po 0x00007fff92b5e0f0
    NSObject
    此时我们又得到了NSObject.由此可见,根元类的 isa指针 指向自己
    此时我们完美验证了上图的isa走向流程

isa总结:对象的isa 指向对象的类--> 对象的类的isa指向元类 ---> 元类的isa指向根元类 ---> 根元类的isa指向自己

类的继承

从上面的经典走位图里面我们也可以看出元类的继承关系:

  • 的继承

继承于父类
父类 继承于 根类
根类继承于nil

  • 元类的继承

元类 继承于父元类
父元类 继承于 根元类
根元类继承于根类
根类继承于 nil

: 关于类的继承我们要明白一点:实例对象之间是没有继承关系的,只有类 之间才有继承关系
通过下面的图我们也可以看到实例对象之间是没有继承关系的

在这里插入图片描述

万物皆对象

我们在上面的走位图中发现,对象元类中都有isa,这是为什么呢? 这就引出了两个结构体_objc_class_objc_object,我们对mian.m进行clang编译后我们会发现对象本质是一个结构体,他的底层会被编译为NSObject_IMPL而 这个NSObject_IMPL,内部包含一个Class 定义的 isa,而Class 是有 objc_class 定义的

struct LGPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString *_name;
};

struct NSObject_IMPL {
	Class isa;
};
typedef struct objc_class *Class;

我们在源码中搜索objc_class,发现objc_class继承于_objc_object,而_objc_object里面包含一个默认的isa:

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 }
} 
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
}

从上面的源码我们也看出了,所有的对象都是从objc_object继承过来的,所以都一定有isa.

类的结构分析

我们先创建一个LGPerson类,然后为它添加属性成员变量实例方法类方法都存在类信息的那个部位:


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

- (void)sayHello;
+ (void)sayHappy;

@end

@implementation LGPerson

- (void)sayHello{
    NSLog(@"LGPerson say : Hello!!!");
}

+ (void)sayHappy{
    NSLog(@"LGPerson say : Happy!!!");
}

@end

探索之前我们先看下类信息里面都包含哪些东西:

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 }
} 
  • isa属性:继承自objc_objectisa指针占 8 字节
  • superclass 属性:指向父类, 指针占 8 字节
  • cache属性:用于缓存方法的,用于加速方法的调用,占 16 字节,具体分析,看cache_t 结构体的分析
  • bits属性:存储类的方法、属性、协议等信息的地方

我们看到bits是存储类的信息的,而bits是有class_data_bits_t定义的,那我们就看下class_data_bits_t的源码:

struct class_data_bits_t {
    friend objc_class;

    // Values are the FAST_ flags above.
    uintptr_t bits;
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
     // 代码自动省略
    }

    const class_ro_t *safe_ro() {
        class_rw_t *maybe_rw = data();
        if (maybe_rw->flags & RW_REALIZED) {
            // maybe_rw is rw
            return maybe_rw->ro();
        } else {
            // maybe_rw is actually ro
            return (class_ro_t *)maybe_rw;
        }
    }
    // 代码自动省略

我们主要看下class_rw_tclass_ro_t:

struct class_rw_t {
	//这里只显示我们需要看到额属性
	const method_array_t methods() const {}

    const property_array_t properties() const {}

    const protocol_array_t protocols() const { }
}



我们现在用lldb来打印下bits里面的信息:

  • 首先平移32位 :
    在这里插入图片描述

  • 接着class_data_bits_t通过data()获取class_rw_t结构体指针:

在这里插入图片描述

  • 然后我们根据class_rw_t中的methods()properties()去获取 方法和属性:
    在这里插入图片描述
    在这里插入图片描述
    我们发现打印出的方法只有sayHello没有、属性只有name,这是因为类方法成员变量存在元类中.
    关于成员变量的探索:

(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100008208 LGPerson
(lldb) p (class_data_bits_t*)0x0000000100008228
(class_data_bits_t *) $1 = 0x0000000100008228
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001018905b0
(lldb) p $2->ro()
(const class_ro_t *) $3 = 0x00000001000080a0
(lldb) p *$3
(const class_ro_t) $4 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100003f83 “\x02”
name = 0x0000000100003f7a “LGPerson”
baseMethodList = 0x00000001000080e8
baseProtocols = 0x0000000000000000
ivars = 0x0000000100008150
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100008198
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $4.ivars
(const ivar_list_t *const) $5 = 0x0000000100008150
(lldb) p *$5
(const ivar_list_t) $6 = {
entsize_list_tt<ivar_t, ivar_list_t, 0> = {
entsizeAndFlags = 32
count = 2
first = {
offset = 0x00000001000081d0
name = 0x0000000100003ef9 “hobby” //这里就是我们要找的成员变量
type = 0x0000000100003f8d “@“NSString””
alignment_raw = 3
size = 8
}
}
}
(lldb)

总结:

  • 属性实例方法存在
  • 成员变量类方法存在元类

[面试题]

类对象内存存在几份?

我们来根据一段代码来验证一下:

//MARK: - 分析类对象内存存在个数
void lgTestClassNum(){
    Class class1 = [LGPerson class];
    Class class2 = [LGPerson alloc].class;
    Class class3 = object_getClass([LGPerson alloc]);
    Class class4 = [LGPerson alloc].class;
    NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
}

看下打印结果:
在这里插入图片描述
发现这个几个class 的地址都是一样的,说明类在内存里面只存在一份

objc_class 与 objc_object 有什么关系?

结构体类型objc_class 继承自objc_object类型,其中objc_object也是一个结构体,且有一个isa属性,所以objc_class也拥有了isa属性

objc_object 与 对象的关系?

所有的对象都来自NSObject,但他们的底层都是来自objc_object,所以对象objc_object是继承关系

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值