iOS OC 对象原理探索三

前言:

前面分析了对象创建时alloc是怎么申请内存空间,通过研究Class的第一个属性是isa,我们都知道isa的指向其类,对象的isa指向类Class, 那么对象创申请完内存空间之后是怎么和isa关联起来的呢?isa指针是怎么创建的呢?
isa指针的具体走向是什么呢?isa指针所占的8字节64位中具体存储了那些信息呢?接下来我们来探索一下:

1. isa指针结构

isa源码

#include "isa.h"
// 联合体  公用 联合体里面最大的那个内存
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

isa底层是联合体,ISA_BITFIELD源码如下:

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

可以看出在不通架构中isa内成员所占字节也不相同,这些成员所占字节之和刚好是64位,8字节。每个成员多占位数和存储内存如下:

     nonpointer        : 1字节;
     表示是否对isa指针开启优化
     0:纯isa指针 1:不止是类对象地址,包含了类信息、对象的引用计数等
     
     has_assoc         : 1字节;
     关联对象标志,0:没有  1:存在
     
     has_cxx_dtor      : 1字节;
     该对象是有有C++或objc的析构器,
     有析构函数:做逻辑出来   无: 可以更快的释放对象

     shiftcls          : 33字节; MACH_VM_MAX_ADDRESS 0x1000000000
     存储类指针的值   开启指针优化情况下,在arm64架构中用s33位来存储类指针
     
     magic             : 6;
     调试器判断f当前对象是真的对象还是没有初始化的空间

     weakly_referenced : 1;
     标志对象是否指向或曾经指向一个ARC的弱引用,无弱引用可更快释放
     
     deallocating      : 1;
     标志对象是否正在是否内存
     
     has_sidetable_rc  : 1;
     当前对象引用计数大于10时,则需要借用该变量的存储进位
     
     extra_rc          : 19
     表示该对象的引用计数值,实际是引用计数减1,
     例: 当前对象引用计数为10,extra_rc = 9,如果引用计数大于10,
     则需要使用has_sidetable_rc进位进位

2. isa的初始化

ias指针初始化分析如下:

isa是联合体,具有互斥性,对cls赋值,就不会对bits和isa内的结构体成员进行赋值。
在对新创建的isa_t newisa(0)进行赋值时,根据SUPPORT_INDEXED_ISA判断,对其进行不通的赋值。

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
        assert(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;

3. isa关联对象和类

上面提到isa指针中shiftcls存储类指针的值,通过打印类.class的值然后>>3,得到的值和通过对isashiftcls成员>>3,再>>20 <<20得到的值相等。

通过研究API object_getClass(object)

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

最终的返回结果是(Class)(isa.bits & ISA_MASK);源码如下:

扫描二维码关注公众号,回复: 10749356 查看本文章
inline Class 
objc_object::ISA() 
{
    assert(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

代码验证:

LGPerson *object = [LGPerson  alloc];

打印结果:


通过打印对象结构,第一个必然是isa指针,通过对isa & 掩码 ISA_MASK 得到的值和打印对象的class一致。由此可以看出isa是通过shiftcls和关联到类的

4. isa的走位

同一个对象可以创建多个?同一个类可以创建多个吗?类只可以创建一个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ckJs0ukG-1586242843847)(https://user-gold-cdn.xitu.io/2019/12/24/16f38705b086af5f?w=604&h=265&f=png&s=95565)]

类的内存,第一个位置必然是`isa`,指向`LGPerson`元类
对象是程序猿根据类实例化的
类是代码编写的,内存中只有一份,是系统创建的
元类是系统编译时,系统编译器创建的,便于方法的编译

对象的isa指向类对象类对象isa指向元类,元类isa指向什么呢?

通过打印类结构,分析如下:

对象isa -->类对象 —>元类–>根元类–>根元类

NSObject 根类 -> 根元类

经典isa走位图

isa:对象 -->类对象 —>元类–>根元类–>根元类
superClass继承关系NSObject 父类为nil根元类的父类NSObject

5. 对象的本质

通过对下面代码的clang -rewrite-objc main.m -o mian.cpp生成的c++文件进行分析

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

@implementation LGPerson

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"123");
    }
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CblpEreT-1586242843857)(https://user-gold-cdn.xitu.io/2019/12/25/16f3bd726ade9bb5?w=955&h=560&f=png&s=132211)]
可以看出对象的底层本质是结构体struct,通过property的属性,底层会自动生成settergetter,并且会生成一个_属性的成员变量,底层编译不会生成相应的 settergetter

6. union联合体补充

1. 什么是联合体
1. union中可以定义多个成员,union的大小由最大的成员的大小决定。
2. union成员共享同一块大小的

内存,一次只能使用其中的一个成员。
3. 对某一个成员赋值,会覆盖其他成员的值(也不奇怪,因为他们共享一块内存。但前提是成员所占字节数
相同,当成员所占字节数不同时只会覆盖相应字节上的值,比如对char成员赋值就不会把整个int成员覆
盖掉,因为char只占一个字节,而int占四个字节)
4. union的存放顺序是所有成员都从低地址开始存放的。

2.联合体的好处
使代码存储数据高效率的同时,有较强的可读性,可以使用共用体来增强代码可读性,同时使用位运算来提高
数据存取的效率
发布了17 篇原创文章 · 获赞 9 · 访问量 170

猜你喜欢

转载自blog.csdn.net/LiangliangSpeak/article/details/105364910