TaggedPointer 指针
- 指针地址 + 值
- 怎么区分 taggedPointer ,在第 0 位的标识是1 代表是 taggedPointer 指针
- 第 1 ~ 3 位的值标识 tagType,
- 不需要执行 retain & release 方法,存放在常量区,系统控制释放
1
2
3
4
5
6
7
8
9OBJC_TAG_NSAtom = 0,
OBJC_TAG_1 = 1,
OBJC_TAG_NSString = 2,
OBJC_TAG_NSNumber = 3,
OBJC_TAG_NSIndexPath = 4
OBJC_TAG_NSManagedObjectID = 5,
OBJC_TAG_NSDate = 6,
...
retain
传入的参数有 isa_t ,判断不是 nonpointer 类型的 isa,直接操作 SideTables
sidetables 不是一张表,如果只有一张,全局对象都需要多线程访问,效率底下,又不能一个对象一张表模拟器是64张,真机是8张,源码中显示的限制
1
2
3
4
5sturct SideTable {
spinlock_t lock; // 自旋锁
RefcountMap refcnts; // 引用计数表
weak_table_t weak_table; // 弱引用表
}判断是否正在释放
从 isa 中找到 extra_rc 的位置,引用计算 +1 ,如果 extra_rc 满了不够用了,需要创建 sideTable 并在 extra_rc 标记,
为什么会吧 extra_rc 的数据一半放入散列表,为了在后续 retain & release 操作的时候尽可能不访问散列表,(开锁解锁耗时)
问题
1 | NSObject *obj = [NSObject alloc]; |
autorelease
- autoReleasePool
- autoreleasepage 存放指针的栈结构
- page 与 page 之间是双向链表结构
- 和线程有关系
1 | class AutoreleasePoolPage; |
- magic 用来验证 autoreleasePoolPage 的结构是否完整
- next 指向最新添加的 autorelease 对象的下一个位置,初始化时指向 begin()
- thread 指当前线程
- parent 指向父结点,第一个 page 的父结点指向 nil
- child 指向子结点,最后一个 page 的子结点指向 nil
- depth 表示深度,从 0 开始,往后递增 1
- hiwat 是 high water mark 最大入栈数量标记
问题
压栈过程(push)
- 一个 autoreleasepool 多个 page 只有一个哨兵对象,放在最开始的位置,为了在 pop 的时候存在边界
- 在 @autoreleasepool 在 push 的时候,先放入一个哨兵对象,创建一个 page ,并标记当前为 hotPage
- 后续再添加对象入栈,如果满了就创建 child page ,继续压栈;
出栈过程(pop)
1.顺序是和出栈相反,do while 循环,从 child 往 parent 的方向,向 page 里面的对象发送 release 消息,直到找到 parent 为空的 page 并且找到哨兵对象停止,其中一次销毁 page 对象
runloop
问题
- runloop 是什么?
__CFRunLoop 是一个结构体
一个 runloop 对象对应多个 runloopMode 一个 mode 对应多个 modeItem ,item 可以是 timer & source & observer
1 | struct __CFRunLoop { |
- runloop 能干什么?
- runloop 和线程之间的关系
- 从 getMainRunLoop 源码中得出,主线程的 runloop 和主线程对应,是一个全局字典的方式映射存储的,key 是线程,value 是 CFRunloopRef 对象