今天看到一篇文章, 里面介绍到arm64后isa是个联合体,就顺便回顾了下TaggedPointer & Weak。
在 arm64 架构之前, isa就是一个普通的指针, 存储着 Class、Meta-Class 对象的内存地址;
从 arm64 架构开始, 对isa进行了优化, 变成了一个 union , 这8个字节(64位)不仅仅用来放地址值(其中33位), 还使用位域来存储更多的信息;
TaggedPointer
NSNumber对象里面有很多成员变量,为啥只占8个字节呢?

-
Tagged Pointer
并不是真正的对象,而是一个伪对象
因为Tagged Pointer
不是一个真正的对象,所以当你访问它的ISA的时候自然就会报上面的错误了。
如果一个数超过了Tagged Pointer
所能表示的范围,又会怎么处理呢?同样做个试验:
NSNumber *bigNumber = @(0xEFFFFFFFFFFFFFFF);
NSLog(@"%p", bigNumber);
打印结果:
0x6000002310c0
我们发现bigNumber
更像一个普通的地址,跟他本身的值并没有什么关系,我们可以打印一下他的ISA,发现是可以打印的:

结论:
- 当8字节可以承载用于表示的数值时,系统就会以
Tagged Pointer
的方式生成指针,如果8字节承载不了时,则又用以前的方式来生成普通的指针。
Weak
Weak的作用、用法这里就不特别介绍了。
当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?
- 调用objc_release(
其实是AutoreleasePool触发的
) - 因为对象的引用计数为0,所以执行dealloc
- 在dealloc中,调用了_objc_rootDealloc函数
- 在_objc_rootDealloc中,调用了object_dispose函数
- 调用objc_destructInstance
- 最后调用objc_clear_deallocating,详细过程如下:
a. 从weak表中获取废弃对象的地址为键值的记录
b. 将包含在记录中的所有附有 weak修饰符变量的地址,赋值为 nil
c. 将weak表中该记录删除
d. 从引用计数表中删除废弃对象的地址为键值的记录
设置weak的流程。
设置weak的时候会有old和new 2个哈希表, 如果已经设置过,清除之前的,然后设置新的(weak_register_no_lock)。
weak_register_no_lock
可以看到在weak_entry_t的结构定义中有联合体,在联合体的内部两种方式来存储弱引用对象的指针地址。
- 定长数组inline_referrers[WEAK_INLINE_COUNT]
WEAK_INLINE_COUNT=4
- 动态数组weak_referrer_t *referrers通过out_of_line()
当弱引用该对象的指针数目小于等于WEAK_INLINE_COUNT时,使用定长数组。当超过WEAK_INLINE_COUNT时,会将定长数组中的元素转移到动态数组中,并之后都是用动态数组存储。
如果referent为nil 或 referent 采用了TaggedPointer计数方式,直接返回,不做任何操作。
如果对象正在析构,则抛出异常。
如果对象不能被weak引用,直接返回nil。
如果对象没有在析构且可以被weak引用,则调用weak_entry_for_referent方法根据弱引用对象的地址从弱引用表中找到对应的weak_entry,如果能够找到则调用append_referrer方法向其中插入weak指针地址。否则新建一个weak_entry。
(没有在析构,同时应该支持weak引用)
网友评论