YYCahe源码分析---总结

本文总结了阅读YYCache源码的心得,重点讨论了LRU和LFU缓存算法的区别,缓存文件置换机制,双链表与单链表的优缺点,并介绍了内存缓存与磁盘缓存的特性。还探讨了互斥锁、信号量在多线程中的应用以及内联函数的作用。参考了多位大佬的分析,有助于深入理解YYCache的设计思想。
摘要由CSDN通过智能技术生成

花了几天的时间看完了,ibireme大佬写的YYCahe、以及其他大佬对YYCahe源码分析,在此记录一下,自己看完的收获。

网上有很多大佬分析了YYCahe 的源码,如何使用,每个类、函数之间的关系等,每个人的理解不太一样,就看自己如何理解了,不再做过多的说明。

以下几点,在很多源码分析中,只是简单的指出了一下,或者是未有说明,自己觉得需要去理解,掌握的东西,写给自己看的 : )

  1. YYMemoryCache,中使用了LRU缓存算法,什么是LRU?那么LFU又是什么?他们的区别是什么?如何简单的使用?什么是缓存文件置换机制?

  2. YYMemoryCache,中使用了双链表处理内存中的,删除、插入等操作,什么是双链表?为何不用单链表?

  3. 在移动端使用缓存,包括了内存缓存、磁盘缓存、它们之间的区别是什么、各有什么优缺点?

  4. YYMemoryCache中,使用互斥锁来处理、查找、删除等操作,什么是互斥锁?有什么作用?dispatch_semaphore、pthread_mutex_lock等区别在哪里?

  5. 其他的一些以前没有接触到的知识点,什么是内联函数?

结构图:
在这里插入图片描述

一、YYMemoryCache,中使用了LRU缓存算法,什么是LRU?那么LFU又是什么?他们的区别是什么?如何简单的使用?什么是缓存文件置换机制
YYMemoryCache中,使用缓存淘汰算法(LRU)、缓存清理策略(分别是count(缓存数量),cost(开销),age(距上一次的访问时间))。

1.LRU
全称是Least Recently Used,即最近最久未使用的意思.
如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰.
如果使用数组来存储数据,给每个数据项标记一个时间戳,每次插入、删除时,需要把数组中存在的数据项的时间戳自增、或自减;插入新数据则需要把时间戳设置为0.
当空间已满,则将时间戳最大的数据项淘汰掉;缺点,需要不停的维护数据项的访问时间戳。

更好的的办法就是,利用链表移动访问时间的数据顺序和hashmap查询是否是新数据项

1.1 算法流程
新数据插入到链表头部;
每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
当链表满的时候,将链表尾部的数据丢弃。
例如:
比如有数据 1,2,1,3,2
此时缓存中已有(1,2)
当3加入的时候,得把后面的2淘汰,变成(3,1)

2.LFU
LFU(Least Frequently Used)最近最少使用算法。它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。

2.1
新加入数据插入到队列尾部(因为引用计数为1);
队列中的数据被访问后,引用计数增加,队列重新排序;
当需要淘汰数据时,将已经排序的列表最后的数据块删除。
例如
比如有数据 1,1,1,2,2,3
缓存中有(1(3次),2(2次))
当3加入的时候,得把后面的2淘汰,变成(1(3次),3(1次))
区别:LRU 是得把 1 淘汰。

3.LRU与LFU 对比:
名词解释: 缓存污染指操作系统将不常用的数据,从内存移到缓存,降低了缓存效率的现象

注意LFU和LRU算法的不同之处,LRU的淘汰规则是基于访问时间,而LFU是基于访问次数的

命中率
LFU: 一般情况下,LFU效率要优于LRU,且能够避免周期性或者偶发性的操作导致缓存命中率下降的问题。但LFU需要记录数据的历史访问记录,一旦数据访问模式改变,LFU需要更长时间来适用新的访问模式,即:LFU存在历史数据影响将来数据的“缓存污染”效用
LRU: 当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重

复杂度:
LFU: 需要维护一个队列记录所有数据的访问记录,每个数据都需要维护引用计数
LRU:实现简单

代价
LFU: 需要记录所有数据的访问记录,内存消耗较高;需要基于引用计数排序,性能消耗较高
LRU:命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部

4.什么是缓存文件置换机制?
是计算机处理缓存存储器的一种机制。
计算机存储器空间的大小固定,无法容纳服务器上所有的文件,所以当有新的文件要被置换入缓存时,必须根据一定的原则来取代掉适当的文件。此原则即所谓缓存文件置换机制

缓存文件置换方法有:
先进先出算法(FIFO): 最先进入的内容作为替换对象
最近最少使用算法(LFU): 最近最少使用的内容作为替换对象
最久未使用算法(LRU): 最久没有访问的内容作为替换对象
非最近使用算法(NMRU):在最近没有使用的内容中随机选择一个作为替换对象

二、什么是双链表、单链表?

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点
删除:在这里插入图片描述

单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;链表是使用指针进行构造的列表;又称为结点列表,因为链表是由一个个结点组装起来的;其中每个结点都有指针成员变量指向列表中的下一个结点

在这里插入图片描述

列表是由结点构成,head指针指向第一个成为表头结点,而终止于最后一个指向nuLL的指针

  1. 双向链表与单向链表相比有以下优势:
    插入删除不需要移动元素外,可以原地插入删除
    可以双向遍历
    存储效率
    存储结构来看,每个双链表的节点要比单链表的节点多一个指针,而长度为n就需要 n*length(这个指针的length在32位系统中是4字节,在64位系统中是8个字节) 的空间,这在一些追求时间效率不高应用下并不适应,因为它占用空间大于单链表所占用的空间;这时设计者就会采用以时间换空间的做法,这时一种工程总体上的衡量

  2. 链表跟数组的区别?
    数组静态分配内存,链表动态分配内存;
    数组在内存中连续,链表不连续;
    数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n);
    数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)

优点:
数组:
随机访问性强(通过下标进行快速定位)
查找速度快

链表:
插入删除速度快(因为有next指针指向其下一个节点,通过改变指针的指向可以方便的增加删除元素)
内存利用率高,不会浪费内存(可以使用内存中细小的不连续空间(大于node节点的大小),并且在需要空间的时候才创建空间)
大小没有固定,拓展很灵活

缺点:
数组
插入和删除效率低(插入和删除需要移动数据)
可能浪费内存(因为是连续的,所以每次申请数组之前必须规定数组的大小,如果大小不合理,则可能会浪费内存)
内存空间要求高,必须有足够的连续内存空间。
数组大小固定,不能动态拓展

链表
不能随机查找,必须从第一个开始遍历,查找效率

三、 在移动端使用缓存,包括了内存缓存、磁盘缓存、它们之间的区别是什么、各有什么优缺点?

缓存的方式分为两种分别为内存缓存磁盘缓存,内存缓存速度快容量小,磁盘缓存容量大速度慢可持久化。

沙盒缓存: 可以看做磁盘缓存的一部分,是一种安全体系。
每一个APP都有一个存储空间,就是沙盒,APP之间不能相互通信。

iOS内存分为5个区:栈区,堆区,全局区,常量区,代码区

栈区:静态内存在栈(stack)上分配;静态内存分配在编译时完成,不占用CPU资源

堆区:动态内存在堆(heap)上分配;动态内存分配在运行时,分配与释放都占用CPU资源

全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。

常量区:常量字符串

代码区:存储代码

沙盒缓存
Documents:用于存储用户数据,iTunes备份和恢复的时候会包括此目录,所以,苹果建议将程序中建立的或在程序中浏览到的文件数据保存在该目录下。
tmp:存放临时文件,这个可以放一些当APP退出后不再需要的文件,iTunes不会备份和恢复此目录,此目录下文件可能会在应用退出后删除
Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出删除

四、YYMemoryCache中,使用互斥锁来处理、查找、删除等操作,什么是互斥锁?有什么作用?dispatch_semaphore、pthread_mutex_lock等区别在哪里?

  1. 什么是锁?

锁是一种同步机制,用于在存在多线程的环境中实施对资源的访问限制。你可以理解成它用于排除并发的一种策略!你可以理解为为了防止多线程访问下资源的抢夺,保持线程同步的方式,以下就是常用的几个锁:

1.@synchronized 关键字加锁
2. NSLock 对象锁
3. NSCondition
4. NSConditionLock 条件锁
5. NSRecursiveLock 递归锁
6. pthread_mutex 互斥锁(C语言)
7. dispatch_semaphore 信号量实现加锁(GCD)
8. OSSpinLock 自旋锁
9.pthread_rwlock 读写锁
10.os_unfair_lock iOS10之后替代OSSPinLock的锁,解决了优先级反转的问题

简单的说明:

POSIX 含义
POSIX线程(英语:POSIX Threads,常被缩写为Pthreads)是POSIX的线程标准,定义了创建和操纵线程的一套API。实现POSIX 线程标准的库常被称作Pthreads,一般用于Unix-like POSIX 系统,如Linux、Solaris。但是Microsoft Windows上的实现也存在,例如直接使用Windows API实现的第三方库pthreads-w32;而利用Windows的SFU/SUA子系统,则可以使用微软提供的一部分原生POSIX API

@synchronized
指令使用的obj为该锁的唯一标识,只有当标识相同时,才为满足互斥,如果线程2中的@synchronized(obj)改为@synchronized(self),刚线程2就不会被阻塞,@synchronized指令实现锁的优点就是我们不需要在代码中显式的创建锁对象,便可以实现锁的机制,但作为一种预防措施,@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象

NSLock
在这里插入图片描述
遵循 NSLocking 协议,lock 方法是加锁,unlock 是解锁。

NSCondition
在这里插入图片描述
一个锁和一个线程检查器
条件对象充当给定线程中的锁和检查点。 锁在测试条件时执行由条件触发的任务,从而保护您的代码。 检查点行为要求在线程继续执行任务之前条件为true。 虽然条件不正确,但线程会阻塞。 它一直被阻塞,直到另一个线程发出条件对象的信号

NSConditionLock
在这里插入图片描述
NSConditionLock 和 NSLock 类似,都遵循 NSLocking 协议,方法都类似,只是多了一个 condition 属性,以及每个操作都多了一个关于 condition 属性的方法,例如 tryLock,tryLockWhenCondition:,NSConditionLock 可以称为条件锁,只有 condition 参数与初始化时候的 condition 相等,lock 才能正确进行加锁操作。而 unlockWithCondition: 并不是当 Condition 符合条件时才解锁,而是解锁之后,修改 Condition 的值

NSRecursiveLock 递归锁
在这里插入图片描述
NSRecursiveLock 可以在一个线程中重复加锁(反正单线程内任务是按顺序执行的,不会出现资源竞争问题),NSRecursiveLock 会记录上锁和解锁的次数,当二者平衡的时候,才会释放锁,其它线程才可以上锁成功

pthread_mutex
在这里插入图片描述
C 语言下多线程加互斥锁的方式,实现和 NSRecursiveLock 类似的效果

dispatch_semaphore
dispatch_semaphore_create(long value);
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
dispatch_semaphore_signal(dispatch_semaphore_t dsema);
GCD 用来同步的一种方式,与他相关的只有三个函数,一个是创建信号量,一个是等待信号,一个是发送信号

OSSpinLock
只有加锁,解锁,尝试加锁三个方法。和 NSLock 不同的是 NSLock 请求加锁失败的话,会先轮询,但一秒过后便会使线程进入 waiting 状态,等待唤醒。而 OSSpinLock 会一直轮询,等待时会消耗大量 CPU 资源,不适用于较长时间的任务

os_unfair_lock
os_unfair_lock iOS 10.0新推出的锁,用于解决OSSpinLock优先级反转问题

  1. 自旋锁(atomic 原子锁)和互斥锁 synchronize对比?

自旋锁:
atomic:原子属性,它默认会为setter方法加锁 (如果不写,就会默认atomic),线程安全,但需要消耗大量的资源。
nonatomic:非原子属性,不会为setter方法加锁,非线程安全。

如果共享数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁,则等待资源的线程会立即执行

线程一直是running(加锁——>解锁),死循环(忙等 do-while)检测锁的标志位

互斥锁:
如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁,则等待资源的线程会被唤醒
线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换(主动出让时间片,线程休眠,等待下一次唤醒),cpu的抢占,信号的发送等开销。

  1. 关于YYMemoryCache中的,pthread_mutex_lock,与YYDiskCache中dispatch_semaphore区别。

YYMemoryCache中自旋锁(OSSpinLock)作为内存缓存的线程锁,但是后来得知其不够安全,所以退而求其次,使用了pthread_mutex。

在YYDiskCache在写入比较大的缓存时,可能会有比较长的等待时间,而dispatch_semaphore在这个时候是不消耗CPU资源的,所以比较适合。

五、其他的一些以前没有接触到的知识点,什么是内联函数?

  1. 在YYMemoryCache中,使用了inline.内联函数
    在这里插入图片描述

内联函数的作用:
解决函数调用效率的问题
函数之间调用,是内存地址之间的调用,当函数调用完毕之后还会返回原来函数执行的地址。函数调用有时间开销,内联函数就是为了解决这一问题。
不用inline修饰的函数, 汇编时会出现 call 指令.调用call指令就是就需要:
(1)将下一条指令的所在地址入栈
(2)并将子程序的起始地址送入PC(于是CPU的下一条指令就会转去执行子程序)

  1. 在YYCahe,大量使用了CF类型的属性,为什么?
    NS是objc的基础库
    CG,CF等是比较底层的C语言的库

在OC中,主要有两种对象:Objective-C 对象和 Core Foundation 对象。Core Foundation 对象主要是由C语言实现的 Core Foundation Framework 的对象,其中也有对象引用计数的概念,只不过不是 Cocoa Framework中Foundation Framework 的 retain/release,而是自身的 CFRetain/CFRelease 接口。

这两种对象间可以互相转换和操作。

例如:
CFDictionary 比 NSDictionary 效率更高.

  1. 宏定义.
    使用__has_include来检查Frameworks是否引入某个类,无需在重复导入。
    #if __has_include(<sqlite3.h>)
    #import <sqlite3.h>
    #else
    #import “sqlite3.h”
    #endif

非常感谢各位大佬的博客,参考了文章部分中的内容:

ibireme大佬对YYCahe的设计思路:
https://blog.ibireme.com/2015/10/26/yycache/
爱奇艺大佬分析:
https://juejin.im/post/5a657a946fb9a01cb64ee761
https://blog.csdn.net/u012441289/article/category/7274351
其他资料:
关于iOS中,各种锁的详细说明:
https://www.jianshu.com/p/a236130bf7a2
https://www.jianshu.com/p/938d68ed832c
https://www.jianshu.com/p/ddbe44064ca4
内联函数:
https://www.jianshu.com/p/d557b0831c6a

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值