文章目录

  • Retain、release复习
  • SideTable
    • spinlock_t slock 自旋锁
      • 分离锁、拆分锁
      • 自旋锁
      • 苹果的选择
    • RefcountMap
    • weak_table_t weak_table
    • 构造和析构函数
    • 最后是锁的操作
    • 小小总结一下SideTable
  • weak部分
    • objc_initWeak
      • objc_storeWeak
      • weak_register_no_lock将新的weak指针添加到弱引用表
        • weak_entry_for_referent取元素
        • append_referrer添加元素
      • weak_unregister_no_lock移除旧weak指针地址
  • dealloc部分
    • 结论

Retain、release复习

我们在Strong实现部分了;了解过了retain和release的源码 先拿个图扔这复习一下

release这里应该是

详解见这个博客
[iOS开发]ARC

关于引用计数的存储方式,清楚看来有两种,一种是通过isa,另一种是通过SideTable
来详细学习一下

SideTable


HashMap(哈希表)
基于数组的一种数据结构,通过一定的算法,把key进行运算得出一个数字,用这个数字做数组下标,将value存入这个下标对应的内存之中


HashTon (哈希桶)
哈希算法算出的数字有可能会重复,对于哈希值重复的数据,如何存入哈希表呢?常用方法有闭散列和开散列等方式,其中采用开散列方式的哈希表称为哈希桶。开散列就是在哈希值对应的位置上,使用链表或数组,将哈希值冲突的数据存入这个链表或者数组中,提高查找效率


为了管理所有对象的引用计数和weak指针,苹果创建了一个全局的SideTables,虽然名字后面又个"s",但其不过还是一个全局的Hash桶,里面的内容装的都是SideTable结构体而已。它使用对象的内存地址当它的key。来管理引用计数和weak指针。

看一下SideTable的内部

struct SideTable {spinlock_t slock;   //自旋锁RefcountMap refcnts;    //存放引用计数weak_table_t weak_table;   //weak_table是一个哈希

先学一下SideTable中的这三个成员变量

spinlock_t slock 自旋锁

我们学习过了操作系统 锁是线程同步时一个重要的工具
操作系统中有五大锁

  • 信号量:
    • 整型信号量S,S<=0表示该资源已被占用,S>0表示该资源可用,pv操作进行访问
    • 记录型信号量 s.value > 0 表示该资源可用的数目;< 0表示在等待链表中已经阻塞的数目
    • AND型信号量,AND型信号量是指同时需要多个资源且每种占用一个资源时的信号量操作。
    • 信号量集 对应有多种资源,相当于记录型的集合
  • 互斥量:和二元信号量类似,唯一不同的是,互斥量的获取和释放必须是在同一个线程中进行的。如果一个线程去释放一个不是其所占有的信号量是无效的。而信号量是可以由其他线程释放的。
  • 临界区:并发执行的进程中,访问临界资源的必须互斥执行的程序段叫临界区
  • 读写锁:解决读者写者问题产生的锁
  • 条件变量:条件变量相当于一种通知机制。多个线程可以设置等待该条件变量,而一旦另外的线程设置了该条件变量(相当于唤醒条件变量)后,多个等待的线程就可以继续执行了。

分离锁、拆分锁

因为对象引用计数相关操作应该是原子性的。不然如果多个线程同时去写一个对象的引用计数,那就会造成数据错乱,失去了内存管理的意义。同时又因为内存中对象的数量是很大的,需要非常频繁的操作SideTables,所以不能对整个Hash表加锁。苹果采用了分离锁技术

  • 分拆锁 (lock splitting) 和分离锁 (lock striping) 是降低线程请求锁的频率从而达到降低锁竞争的两种方式。相互独立的状态变量,应该使用独立的锁进行保护。但有时开发者会错误的使用一个锁保护所有的状态变量。对于这些锁需要仔细分配,以降低发生死锁的风险
  • 如果一个锁守护多个相互独立的状态变量,你可能能够通过分拆锁,使每一个锁守护不同的变量。这样可以使每一个锁被请求的频率都变小了。分拆锁对于中等竞争强度的锁,能够有效的把它们大部分转化为非竞争的锁,使性能和可可伸缩性都得到了提高。
  • 分拆锁有时候可以被扩展,分成若干加锁块的集合,并且它们归属于相互独立的对象,这种情况就是分离锁。

我们将每个SideTable里的每个对象的引用计数都加一把锁,这就是分拆锁,虽然安全 但是消耗很大

我们给每个SideTable加上一把锁,只让某个SideTable不能多次访问,这就是分离锁

自旋锁

自旋锁和互斥锁

  • 相同点:都能保证同一时间只有一个线程访问共享资源。都能保证线程安全。
  • 不同点:
    • 互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁,则等待资源的线程会被唤醒。
    • 自旋锁:如果共享数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁,则等待资源的线程会立即执行。
  • 自旋锁的效率高于互斥锁。但是我们要注意由于自旋时不释放CPU,因而持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在哪里自旋,这就会浪费CPU时间。
  • 在操作引用计数的时候对SideTable加锁,避免数据错误

苹果的选择

对于每个SideTable,中间都有自旋锁
同样也使用了分离锁给单个的SideTable上锁

安全+效率很合理

RefcountMap

来了解一下这个图

DisguisedPtr<objc_object>为key的hash表,用来存储OC对象的引用计数
不知道DisguisedPtr<objc_object>是什么,但是我们已经对retain中存储引用计数的方式十分清晰了,如果未开启isa优化 或 在isa优化情况下isa_t的extra_rc引用计数加一后向上溢出了,才会存入这个哈希表中。

引用计数表的查找和插入过程

这个问题其实要牵扯sidetable中引用计数存储的存储、取值
感觉没啥用 我真看不明白了 博客也就那一篇
主要流程记录一下吧

  • 哈希值相同,一个被存了,另外几个就要重新进行哈希算法去找合适位置
  • 如果此时c对象被释放掉了 就会把这个位置的key设置为墓碑
  • 如果c对象销毁后将下标2的桶设置为空桶,此时为e对象增加引用计数,根据哈希算法找到下标为2的桶时,就会直接插入。
  • 如果此时初始化了一个新的对象f来增加对象的引用计数,如果没设置为空桶,设置为了墓碑,我们会记录下来下标2,然后继续走哈希,查到空桶,就证明表里没有f对象,我们直接拿f记录的下标2进行插入即可。
  • 墓碑可以帮我们利用已经释放掉的空间

weak_table_t weak_table

储存对象弱引用指针的hash表。weak功能实现的核心数据结构。

看一下wewak_table_t

struct weak_table_t {weak_entry_t *weak_entries;  //连续地址空间的头指针, 数组//管理所有指向某对象的weak指针,也是一个hashsize_t    num_entries;  //数组中已占用位置的个数uintptr_t mask;  //数组下标最大值(即数组大小 -1)uintptr_t max_hash_displacement;  //最大哈希偏移值
};

weak_table_t中并没有直接通过数组存放weak指针,而是通过结构体来存放weak指针
两个参数

  • location:__weak指针的地址,存储指针的地址,这样便可以再最后将其指向的对象置nil
  • newObj: 所引用的对象
struct weak_entry_t {DisguisedPtr<objc_object> referent; //被指对象的地址。前面循环遍历查找的时候就是判断目标地址是否和他相等。union {struct {weak_referrer_t *referrers; //可变数组,里面保存着所有指向这个对象的弱引用的地址。当这个对象被释放的时候,referrers里的所有指针都会被设置成nil。//哈希的目的是清除一个weak指针//指向 referent 对象的 weak 指针数组uintptr_t        out_of_line_ness : 2; //这里标记是否超过内联边界, 下面会提到uintptr_t        num_refs : PTR_MINUS_2; //数组中已占用的大小uintptr_t        mask; //数组下标最大值(数组大小 - 1)uintptr_t        max_hash_displacement; //最大哈希偏移值};struct {// out_of_line_ness field is low bits of inline_referrers[1]weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT]; //只有4个元素的数组,默认情况下用它来存储弱引用的指针。当大于4个的时候使用referrers来存储指针。//当指向这个对象的weak指针不超过4个,则直接使用数组inline_referrers,省去hhash};};

union共用体 也是提醒我们苹果是使用同一段内存去存放不同的信息

中间有两个数组
weak_referrer_t *referrersweak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
在weak指针个数小于4的时候会存入第二个数组,省去了hash,提高了存储效率,大于4的时候才会存入referrers当中

从中我们也可以看出来弱引用表的结构是一个hash结构的表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。

构造和析构函数

// 构造函数SideTable() {memset(&weak_table, 0, sizeof(weak_table));}//析构函数(看看函数体,苹果设计的SideTable其实不希望被析构,不然会引起fatal 错误)~SideTable() {_objc_fatal("Do not delete SideTable.");}

所以SideTable

[iOS开发]weak底层原理相关推荐

  1. iOS arc weak指针原理

    iOS arc weak指针原理 ARC 都帮我们做了什么? weak是什么? weak是怎么实现的? 1. weak原理简介 2. weak简单测试 3. weak原理分析 3.1 weak指针帮我 ...

  2. 海量数据去重,hash、布隆过滤器以及hyperloglog丨c/c++linux服务器开发丨后端开发丨Linux后台开发丨底层原理

    海量数据去重,hash.布隆过滤器以及hyperloglog 视频讲解如下,点击观看: 海量数据去重,hash.布隆过滤器以及hyperloglog丨c/c++linux服务器开发丨后端开发丨Linu ...

  3. redis,memcached到nginx,底层网络io中剥离精髓丨C/C++Linux丨C++后端开发丨Linux服务器开发丨底层原理

    redis,memcached到nginx,底层网络io中剥离精髓 1. redis单线程网络的优缺点 2. memcached多线程网络的并发优势 3. nginx多进程网络的优势 视频讲解如下,点 ...

  4. 浅析“分布式锁”的实现方式丨C++后端开发丨底层原理

    线程锁.进程锁以及分布式锁相关视频讲解:详解线程锁.进程锁以及分布式锁 如何高效学习使用redis相关视频讲解:10年大厂程序员是如何高效学习使用redis Linux服务器开发高级架构学习视频:C/ ...

  5. iOS进阶之底层原理-weak实现原理

    基本上每一个面试都会问weak的实现原理,还有循环引用时候用到weak,今天我们就来研究下weak的实现原理到底是什么. weak入口 我们在这里打个断点,然后进入汇编调试. 这里就很明显看到了入口, ...

  6. 如何接入微信公众号开发?底层原理是什么?

    要接入微信公众号开发,您需要完成以下几个步骤: 注册微信公众平台账号:首先,您需要在微信公众平台上注册一个账号,并创建一个公众号.在注册过程中,您需要提供相关的身份信息和认证材料,以便微信审核和认证您 ...

  7. iOS 进阶之底层原理一OC对象原理alloc做了什么

    人狠话不多,直接上干货.这是第一篇,之后还会持续更新,当作自己学习的笔记,也同时分享给大家,希望帮助更多人. 首先,我们来思考,下面这段代码的输出是否相同.答案很明显,p1.p2.p3是指向相同的对象 ...

  8. iOS进阶之底层原理-block本质、block的签名、__block、如何避免循环引用

    面试的时候,经常会问到block,学完本篇文章,搞通底层block的实现,那么都不是问题了. block的源码是在libclosure中. 我们带着问题来解析源码: blcok的本质是什么 block ...

  9. iOS进阶之底层原理-线程与进程、gcd

    线程与进程 线程的定义 线程是进程的基本单位,一个进程的所有任务都在线程中执行 进程要想执行任务,必须的有线程,进程至少要有一条线程 程序启动默认会开启一条线程,也就是我们的主线程 进程的定义 进程是 ...

  10. iOS进阶之底层原理-应用程序加载(dyld加载流程、类与分类的加载)

    iOS应用程序的入口是main函数,那么main函数之前系统做了什么呢? 我们定义一个类方法load,打断点,查看栈进程,我们发现dyld做了很多事,接下来就来探究到底dyld做了什么. 什么是dyl ...

最新文章

  1. 什么是导师负责制_为什么一个导师是不够的
  2. 使用joda-time工具类 计算时间相差多少 天,小时,分钟,秒
  3. oracle移植mysql方案_系统从MySQL迁移至ORACLE实现方案
  4. python入门练习题3(函数)
  5. 巨杉数据库 CTO 王涛:新一代分布式数据库
  6. python类的成员没有访问控制限制_Python 访问限制 private public的详细介绍
  7. [模拟][字符串]计算器的改良
  8. 软件实习周软件设计报告
  9. flash activex java_adobe flash player activex
  10. 百度地图点聚合仿链家定位点多级聚合,且滑动、刷新加载定位点
  11. 【Matlab】线性回归之最小二乘法的应用与验证
  12. app内嵌h5一键加QQ群
  13. 转:稻盛和夫:幸福的那些“法”,背后承载的“道”是什么?
  14. vue前端生成二维码,实现扫码下载功能
  15. ESP32CAM摄像头图像实时传输
  16. DP之Warshall算法和Floyd算法
  17. Docker的常用命令大全
  18. php中下载xls某个文件,php下载excel文件
  19. 财路网每日原创推送: 创世区块10年:记住这群加密狂魔
  20. Delegate.Combine

热门文章

  1. 三国鼎立企查查、天眼查、微猫的三国大战
  2. Unity3d之贴图融合
  3. java oracle spatial_安装Oracle Spatial数据组件
  4. minecraft pythonapl_Minecraft API
  5. 树莓派搭建http服务器实现网页监控摄像头
  6. 开发一个APP要多少钱?
  7. Android向系统日历添加日程提醒事件
  8. c 语言中strcat的用法,strcat 在 C 语言中是什么意思?
  9. Python-基于request豆瓣电影票房信息爬取,简单粗暴
  10. 数据结构的小知识点(初学者使用)“朝闻道”知识分享大赛