文章目录

  • 前言
    • 关联对象的底层原理
    • weak的实现原理
    • KVO的实现原理
    • iOS App签名的原理
    • 对象引用计数存储的位置
    • Runloop与线程的存储关系
    • NSDictionary的原理
  • 哈希表
    • 哈希表定义
    • 哈希表优缺点
    • 哈希查找步骤
    • 哈希表的存储过程
    • 哈希表的实现
    • 负载因子 = 总键值对数/数组的个数
    • 哈希冲突的解决方法
      • 开散列
      • 闭散列
      • 再哈希法
      • 建立公共溢出区
      • 开闭散列二者的比较
        • 拉链法的优点:
        • 拉链法的缺点:
        • 线性探测法的缺点
  • NSDictionary

前言

天天听安卓的同学整Hash,猛的发现iOS也有很多底层原理也是Hash来实现的,好好学一下。

iOS中也用到了很多Hash表。

关联对象的底层原理

[iOS开发]Category、Extension和关联对象

关联对象采用的是HashMap嵌套HashMap的结构存储数据的,简单来说就是根据对象从第一个HashMap中取出存储所有关联对象的第二个HashMap,然后根据属性名从第二个HashMap中取出属性对应的值和策略。

问题:为什么关联对象没有weak属性?

weak的实现原理

weak底层原理

weak采用的是一个全局的HashMap嵌套数组的结构存储数据的。销毁对象(weak指针指向的对象)的时候,根据对象从HashMap中找到存放所有指向该对象的weak指针的数组,然后将数组中的所有元素(weak指针)都置为nil

weak最大的特点就是在对象销毁的时候,自动置nil,减少访问野指针的风险,这也是设计weak的初衷。weak指针置nil的基本步骤:

  • 对象dealloc的时候,从全局的HashMap中,根据一个唯一代码对象的值作为key,找到存储所有指向该对象的weak指针的数组
  • 将数组中的所有元素都置nil

苹果对于weak的实现其实类似于通知的实现,指明谁(weak指针)要监听谁(赋值对象)什么事件(dealloc操作)执行什么操作(置nil)

KVO的实现原理

GNUstep KVC/KVO探索(二):KVO的内部实现

iOS App签名的原理

一致性Hash算法 + 非对称加密算法

iOS App 签名的原理

对象引用计数存储的位置

if 对象支持TaggedPointer {return 直接将对象的指针值作为引用计数返回
}
else if 设备是64位环境 && Objective-C2.0 {return 对象isa指针的一部分空间(bits_extra_rc)
}
else {return hash表
}

Runloop与线程的存储关系

线程和Runloop之间是一一对应的关系,其关系是保存在一个全局的Dictionary里。

线程在刚创建是没有RunLoop,如果我们不主动获取,那他就一直都不会有。RunLoop的创建是发生在第一次获取时,RunLoop的销毁时发生在线程结束时。我们只能在一个线程内部获取其RunLoop。

NSDictionary的原理

学完Hash之后我们来学习具体的实现

哈希表

前面说了那么多的底层原理都与Hash表有关,我们来详细学一下哈希表

哈希表定义

哈希表(hash tabl,也叫散列表),是根据键(key)直接访问在内存存储位置的数据结构。哈希表本质是一个数组,数组中的每一个元素成为一个箱子,箱子中存放的是键值对。根据下标index从数组中取value。关键是如何获取index,这就需要一个固定的函数(哈希函数),将key转换成index。不论哈希函数设计的如何完美,都可能出现不同的key经过hash处理后得到相同的hash值,这时候我们就需要处理哈希冲突。

哈希表优缺点

优点:把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间;而代价仅仅是消耗比较多的内存。然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的。另外,编码比较容易也是它的特点之一。

缺点:哈希表通常是基于数组,数组创建后难于扩展。也没有一种简便的方法可以以任何一种顺序来遍历哈希表。

所以,如果不需要有序遍历数据,并且可以提前预测数据量的大小,那么哈希表在速度和易用性方面是无与伦比的。

哈希查找步骤

  1. 使用哈希函数将被查找的键转换为数组的索引,理想情况下(hash函数设计合理)不同的键映射的数组下标也不同,所有的查找时间复杂度为O(1)。但是实际情况下不是这样的,所以哈希查找的第二部就是处理哈希冲突。
  2. 处理哈希冲突有很多方法,比如拉链法、线性探测法…

哈希表的存储过程

  1. 使用hash函数根据key得到哈希值h
  2. 如果箱子的个数为n,那么值应该存放在底(h%n)个箱子中。h%n的值范围为[0,n-1]。
  3. 如果该箱子非空(已经存放了一个值)即不同的key得到了相同的h产生了哈希冲突,此时需要使用拉链法或者开放定址线性探测法解决冲突

哈希表的实现

哈希表的底层实际上是基于数组来存储的,当插入键值对时,并不是直接插入该数组中,而是通过对键进行Hash运算得到Hash值,然后和数组容量取模,得到在数组中的位置后再插入。取值时,先对指定的键求Hash值,再和容量取模得到底层数组中对应的位置,如果指定的键值与存贮的键相匹配,则返回该键值对,如果不匹配,则表示哈希表中没有对应的键值对。这样做的好处是在查找、插入、删除等操作可以做到 O ( 1 ) O(1) O(1),最坏的情况是 O ( n ) O(n) O(n),当然这种是最极端的情况,极少遇到。

负载因子 = 总键值对数/数组的个数

负载因子是哈希表的一个重要属性,用来衡量哈希表的空/满成都,一定程度也可以体现查询的效率。负载因子越大,意味着哈希表越满,越容易导致冲突,性能也就越低。所以当负载因子大于某个常数(一般是0.75)时,哈希表将自动扩容。哈希表扩容是,一般会创建两倍于原来的数组长度。这个过程被称为重哈希(rehash)。

哈希表扩容在数组比较多的时候需要重新哈希并移动数据,性能影响较大。

哈希表扩容虽然能够使负载因子降低,但并不总能有效提高哈希表的查询性能。比如哈希函数设计的不合理,导致所有的key计算出的哈希值都相同,那么即使扩容他们的位置还是在同一条链表上,变成了线性表,性能也极低,查询时候的时间复杂度就变成了O(n)。

哈希冲突的解决方法

大类分为四种 开散列、闭散列、再哈希法、建立公共溢出区

开散列

开散列也叫链地址法,也叫拉链法、哈希桶。

简单来说就是 数组 + 链表。将键通过hash函数映射为大小为M的数组的下标索引,数组的每一个元素指向一个链表,链表中的每一个节点都存储着hash出来的索引值为结点下标的键值对。

JAVA 8解决哈希冲突采用的就是拉链法。在处理哈希函数设计不合理导致链表很长时(链表长度超过8切换为红黑树,小于6重新退化为链表)。将链表切换为红黑树能够保证插入和查找的效率,缺点是当哈希表比较大时,哈希表扩容会导致瞬时效率降低。

Redis解决哈希冲突采用的也是拉链法。通过增量式扩容解决了java 8中的瞬时扩容导致的瞬时效率降低的缺点,同时拉链法的实现方式(新插入的键值对放在链表头部)带来了两个好处:
一、 头插法可以节省插入耗时。如果插到尾部,则需要时间复杂度为O(n)的操作找到链表的尾部,或者需要额外的内存地址来保存尾部链表的位置。
二、头插法可以节省查找耗时。最新插入的数据往往可能频繁的被查询。

闭散列

闭散列也叫开放地址法、线性探测法。

当发生哈希冲突时,且哈希表未满,可以通过探测的方法吧key存放在冲突位置的下一个位置中。

方法一:线性探测
从发生冲突的位置开始,一次向后探测,直到寻找到下一个空位置为止。当然这种方法比较简单,也有很大的缺陷,就是容易造成数据的堆积,使得关键字需要多次比较,这样以来就导致搜索效率低

方法二:二次探测

方法三:伪随机探测
建立一个伪随机数发生器(如 i = (i + p) % m),生成一个伪随机序列,并给定一个随机数做起点,每次加上这个伪随机数,++就可以了。

再哈希法

再哈希法其实很简单,就是再使用哈希函数去散列一个输入的时候,输出是同一个位置就再次哈希,直至不发生冲突位置

缺点:每次冲突都要重新哈希,计算时间增加。

建立公共溢出区

这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。

开闭散列二者的比较

拉链法的优点:

与线性探测法相比,拉链法有如下几个优点:

  • 处理冲突简单,且无堆积现象,即非同义词绝不会发生冲突,因此平均查找长度较短;
  • 由于拉链法中各链表上的结点空间是动态申请的,所以更适合于造表钱无法确定表长的情况
  • 线性探测法为减少冲突,要求装填因子较小,故当结点规模较大时会浪费很多空间。二拉链法中可取a>=1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间
  • 在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。而对开放定址线性探测发构造的散列表,删除结点不能简单地将被删结 点的空间置为空,否则将截断在它之后填人散列表的同义词结点的查找路径。这是因为各种开放定址线性探测发中,空地址单元(即开放地址)都是查找失败的条件。因此在用开放定址线性探测发处理冲突的散列表上执行删除操作,只能在被删结点上做删除标记,而不能真正删除结点。

拉链法的缺点:

指针需要额外的空间,故当结点规模较小时,开放定址线性探测法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址线性探测法中的冲突,从而提高平均查找速度。

线性探测法的缺点

  1. 容易产生堆积问题
  2. 不适于大规模的数据存储
  3. 散列函数的设计对冲突会有很大的影响
  4. 插入时可能会出现多次冲突的现象,删除的元素是多个冲突元素中的一个,需要对后面的元素作处理,实现较复杂
  5. 结点规模很大时会浪费很多空间

NSDictionary

[iOS开发]iOS中的Hash相关推荐

  1. iOS开发 Xcode8中遇到的问题及改动

    2019独角兽企业重金招聘Python工程师标准>>> iOS开发 Xcode8中遇到的问题及改动 新版本发布总会有很多坑,也会有很多改动. 一个一个填吧... 一.遇到的问题 1. ...

  2. iOS开发——基础篇——iOS开发 Xcode8中遇到的问题及改动

    iOS开发 Xcode8中遇到的问题及改动 新版本发布总会有很多坑,也会有很多改动. 一个一个填吧... 一.遇到的问题 1.权限以及相关设置 iOS10系统下调用系统相册.相机功能,或者苹果健康都会 ...

  3. iOS开发--iOS应用架构谈 view层的组织和调用方案

    前言 <iOS应用架构谈 开篇>出来之后,很多人来催我赶紧出第二篇.这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分. 现在好啦,第二 ...

  4. iOS开发- ios学习资源(持续更新)

    mark一些自己在学习过程中收集的资源.免得需要的时候没地方找. 持续更新.(最新更新时间: 2014.4.4) 1.苹果官方文档 构建iOS程序:下面的这篇文章介绍了 iOS 程序开发的过程: St ...

  5. iOS开发--iOS通知中心扩展制作入门(iOS Extensions)

    个人笔记: 通知中心扩展,相当于一个特殊的独立的App存在.他和主App之间通讯可以通过OpenURL来实现.而在通知中心内部,像App一样,该咋样还是咋样(如异步网络请求, 自动布局等).  之所以 ...

  6. iOS开发--iOS及Mac开源项目和学习资料

    文/零距离仰望星空(简书作者) 原文链接:http://www.jianshu.com/p/f6cdbc8192ba 著作权归作者所有,转载请联系作者获得授权,并标注"简书作者". ...

  7. iOS开发- ios学习资源

    1.苹果官方文档 构建iOS程序:下面的这篇文章介绍了 iOS 程序开发的过程: Start Developing iOS Apps Today 构建Mac OS X程序:下面这篇文章介绍了Mac O ...

  8. iOS开发UI中懒加载的使用方法

    1.懒加载基本 懒加载--也称为延迟加载,即在需要的时候才加载(效率低,占用内存小).所谓懒加载,写的是其getter方法.说的通俗一点,就是在开发中,当程序中需要利用的资源时.在程序启动的时候不加载 ...

  9. iOS开发Storyboard中UITableView顶部默认空白 - 芒果iOS

    [主要内容:] 1. 问题描述 2.问题分析 3. 解决问题办法 一.问题描述 前两天开发的时候在StoryBoard中创建了一个UITableView,但是拖到Controller里边之后,UITa ...

最新文章

  1. 【CVPR2020】30篇最新论文抢先看!!!
  2. Pentaho Data Integration 界面开发
  3. ThinkJS前端搭配vue时的Nginx配置
  4. Java中可怕的双重检查锁定成语
  5. 计算当前序列的字典序序号(洛谷P2524题题解,Java语言描述)
  6. 如何保证数据最终一致性(分布式事务)
  7. 用户输入的数是否为合数php,php怎么判断一个数是不是素数?
  8. Python中threading的join和setDaemon的区别及用法[例子]
  9. Atitit.图片木马的原理与防范 attilax 总结
  10. Photometric Stereo 光度立体三维重建(四)——光源标定
  11. MODSCAN32的应用
  12. 桌面计算机图标无法附到任务栏,win7系统电脑快捷方式无法添加到任务栏的解决方法...
  13. 云原生 Kuma 简单部署与使用
  14. 谷歌浏览器翻译插件 划词翻译
  15. vm时序数据库-导入数据
  16. python画五角星
  17. 【温故知新】—— React/Redux/React-router4基础知识独立团Demo
  18. 【ROM制作工具】如何将system.new.dat和system.img实现相互转换教程
  19. 【刷机】小米降级刷机 线刷 图解 MIUI降级刷机
  20. 2015-12-12 java hibernate 微信表情昵称保存

热门文章

  1. 攻防世界(动态调试题)
  2. 《云计算核心技术剖析》迷你书连载三 – 云计算的商业模式
  3. 分析11.2.0.3 rac CRS-1714:Unable to discover any voting files
  4. Tensorflow深度学习之二十五:tf.py_func
  5. DASCTF Oct X 吉林工师 欢迎来到魔法世界 部分wp
  6. 谷歌多账户登陆_如何一次登录多个Google帐户
  7. 蓝桥ROS机器人之古月居ROS入门21讲
  8. 火车头采集翻页内容_火车头采集器教程:使用分页采集有分页的数据
  9. 钱多多第二阶段冲刺03
  10. 0.前端简历编写和面试前准备