ios开发缓存策略,ios缓存机制

iOS NSCache 缓存策略

1.概念:NSCache缓存策略中主要使用的是_GSCachedObject类,下图是_GSCachedObject的源码,定义中重点的分别是缓存的访问次数,缓存当前消耗的大小,是否能够被清除的标记

创新互联公司从2013年创立,是专业互联网技术服务公司,拥有项目网站设计、成都网站设计网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元泰安做网站,已为上家服务,为泰安各地企业和个人服务,联系电话:18980820575

2.核心缓存策略源码

- (void)_evictObjectsToMakeSpaceForObjectWithCost: (NSUInteger)cost

{

/** 计算需要清除的空间 */

NSUInteger spaceNeeded = 0;

/** 缓存数量 */

NSUInteger count = [_objects count];

if (_costLimit 0 _totalCost + cost _costLimit)

{

/** spaceNeeded:计算需要清除的空间 = _totalCost:总的消耗内存大小+已有的 -_costLimit:限制消耗大小 */

  spaceNeeded = _totalCost + cost - _costLimit;

}

// Only evict if we need the space.

if (count 0 (spaceNeeded 0 || count = _countLimit))

{

  NSMutableArray *evictedKeys = nil;

  // Round up slightly.

    /**

        averageAccesses:平均访问次数

        _totalAccesses:所有访问次数的总和

        count:内存中的缓存对象

    几乎淘汰一半的缓存,所以乘0.2

    (_totalAccesses / (double)count) * 0.2)可能为0,所以+1

    */

  NSUInteger averageAccesses = ((_totalAccesses / (double)count) * 0.2) + 1;

  NSEnumerator *e = [_accesses objectEnumerator];

  _GSCachedObject *obj;

if (_evictsObjectsWithDiscardedContent)

{

evictedKeys = [[NSMutableArray alloc] init];

}

  while (nil != (obj = [e nextObject]))

{

// Don't evict frequently accessed objects.

    /** 当前的访问次数 是否小于平均的访问次数 且 当前对象可移除 */

if (obj-accessCount averageAccesses obj-isEvictable)

{

        /** 发送通知,释放内存 */

  [obj-object discardContentIfPossible];

  if ([obj-object isContentDiscarded])

{

NSUInteger cost = obj-cost;

// Evicted objects have no cost.

obj-cost = 0;

// Don't try evicting this again in future; it's gone already.

obj-isEvictable = NO;

// Remove this object as well as its contents if required

if (_evictsObjectsWithDiscardedContent)

{

  [evictedKeys addObject: obj-key];

}

_totalCost -= cost;

// If we've freed enough space, give up

        /** 消耗的 需要清除的空间 */

if (cost spaceNeeded)

{

  break;

}

spaceNeeded -= cost;

}

}

}

  // Evict all of the objects whose content we have discarded if required

  if (_evictsObjectsWithDiscardedContent)

{

NSString *key;

e = [evictedKeys objectEnumerator];

while (nil != (key = [e nextObject]))

{

  [self removeObjectForKey: key];

}

}

[evictedKeys release];

}

}

2.淘汰策略:在while循环中通过比对cost spaceNeeded来进行缓存对象obj的移除

open func setObject(_ obj: ObjectType, forKey key: KeyType, cost g: Int) {

    let g = max(g, 0)

    let keyRef = NSCacheKey(key)

    /** 加锁 */

    _lock.lock()

  let costDiff: Int

    /** 查找缓存列表中是否有key对应的缓存对象,有的话替换 */

  /**

  _entries:

        key:NSCacheKey

        value:NSCacheEntry

    */

  if let entry = _entries[keyRef] {

        costDiff = g - entry.cost

        entry.cost = g

      entry.value = obj

      if costDiff != 0 {

            /** 移除旧的缓存 */

            remove(entry)

            /** 添加新的缓存 */

            insert(entry)

        }

    } else {

        let entry = NSCacheEntry(key: key, value: obj, cost: g)

        _entries[keyRef] = entry

        insert(entry)

      costDiff = g

    }

  _totalCost += costDiff

    /** totalCostLimit:所有消耗大小限制 ,_totalCost:当前消耗,*/

    var purgeAmount = (totalCostLimit 0) ? (_totalCost - totalCostLimit) : 0

    while purgeAmount 0 {

        /** head:链表头结点 */

        if let entry = _head {

            /** 回调函数 */

            delegate?.cache(unsafeDowncast(self, to:NSCacheAnyObject, AnyObject.self), willEvictObject: entry.value)

          _totalCost -= entry.cost

            purgeAmount -= entry.cost

            /** 移除缓存对象 */

            remove(entry) // _head will be changed to next entry in remove(_:)

            /** 表中entry对应的key也置为nil */

            _entries[NSCacheKey(entry.key)] = nil

        } else {

            break

        }

    }

    /** countLimit:缓存数量限制,_entries.count:当前缓存对象的数量 */

    var purgeCount = (countLimit 0) ? (_entries.count - countLimit) : 0

    while purgeCount 0 {

        /** head:链表头结点 */

        if let entry = _head {

            /** 回调函数 */

            delegate?.cache(unsafeDowncast(self, to:NSCacheAnyObject, AnyObject.self), willEvictObject: entry.value)

          _totalCost -= entry.cost

            /** 每移除一次,缓存数量 - 1 */

            purgeCount -= 1

            /** 移除缓存对象 */

            remove(entry) // _head will be changed to next entry in remove(_:)

            /** 表中entry对应的key也置为nil */

            _entries[NSCacheKey(entry.key)] = nil

        } else {

            break

        }

    }

    /** 解锁 */

    _lock.unlock()

}

通过insert函数可以看出,通过cost排序,在外部会优先删除占用内存小的缓存对象

swift缓存策略:

1.通过totalCostLimit所有的消耗大小限制和当前总消耗大小做比对,大于零进行while循环移除entry缓存对象

2.通过countLimit缓存数量限制和当前缓存对象的数量大小做差值,大于零进行while循环移除entry缓存对象

iOS web缓存策略以及手动清除缓存

当我们使用webview加载html资源时,本质上就是向服务器索取资源的http请求过程,如果我们不注意资源的缓存策略的话,就可能会造成这样那样的问题,比如:实时性要求较高的功能却老是走缓存不更新,有些基本不会变动的页面却又每次都重新去服务器拉请求。

iOS自带的缓存策略,提供了一个内存和磁盘混合的缓存,一共有7种缓存策略,使用较多的是其中的四种( 下方编号1,2,5,6 )

上面介绍了iOS自带的缓存控制 NSURLRequestCachePolicy ,也说到当 NSURLRequestCachePolicy 设为默认的 NSURLRequestUseProtocolCachePolicy 时,主要是根据http的缓存策略来决定是否使用缓存。

那么就简单的介绍一下,http的缓存控制和缓存校验。

在http中,控制缓存开关的字段有两个,Pragma和Cache-Control

Pragma有两个字段no-cache和expires,当pragma为no-cache时表示禁用缓存,expires的值是一个GMT时间,表示该缓存的有效时间。但是已经被逐步抛弃了,有些网站为了向下兼容还保留了这两个字段。

Cache-Control除了在响应中使用,在请求中也可以使用。

在请求中使用,Cache-Control可选的值有:

在响应中使用,Cache-Control可选的值有:

在缓存中,我们需要一个机制来验证缓存是否有效。比如服务器的资源更新了,客户端需要及时刷新缓存;又或者客户端的资源过了有效期,但服务器上的资源还是旧的,此时不需要重新发送。缓存校验就是用来解决这些问题的,在http1.1中,主要关注下 Last-Modified 和 etag 这两个字段。

服务端在返回资源时,会将该资源的最后更改时间通过 Last-Modified 字段返回给客户端。客户端下次请求时通过 If-Modified-Since 或者 If-UnModified-Since 带上 Last-Modified ,服务端检查该时间是否与服务器的最后修改时间一致:如果一致,则返回304状态码,不反悔资源;如果不一致,则返回200和修改后的资源,并带上新的时间。

单纯的以修改时间来判断还是有缺陷,比如文件的最后修改时间变了,但内容没变。对于这样的情况,我们可以使用etag来处理。

etag的方式是这样:服务器通过某个算法对资源进行计算,取得一串值(类似于文件的md5值),之后将该值通过etag返回给客户端,客户端下次请求时通过If-None-Match或If-Match带上该值,服务器对该值进行对比校验:如果一致则不要返回资源。

当我们的webview缓存到一定的峰值的时候,需要手动的清除一下wenview的缓存,方法如下:

找出web缓存的路径,清空该路径

webKit除了清除缓存的API

觉得有用,请帮忙点亮红心

Better Late Than Never!

努力是为了当机会来临时不会错失机会。

共勉!

ios中关于方法缓存cache策略

文章部分出于自己的理解,有不对的地方,希望大家指正。

实例对象我们可以看作是一个指针,实例对象通过isa指针指向类对象,类对象通过isa指针指向元类对象,类对象和元类对象本身其实都是objc_class结构体,里面存放着我们需要的方法列表等。

那么当我们方法调用的时候,方法是如何缓存的呢?

当我们方法调用的时候,首先通过isa指针找到类对象,然后在类对象的方法列表里面查找相对应的方法,找到以后会加入到我们方法缓存里面,等下次我们再调用的时候,首先会先从缓存里面去查找相关方法,更加的效率。

那如果我们调用的是父类里面的方法呢,其实父类对象里面的方法是不可以缓存到子类方法缓存列表里面的,所以,当我们调用的是父类对象里面的方法的时候,其方法不会在子类对象方法缓存列表里面进行缓存。

元类对象的相关缓存同类对象。

至于缓存扩容和具体的缓存策略:

在arm64结构,也就是真机环境下,刚开始初始化的缓存方法的容器的长度2,当容器的长度小于8时,是满容量了才扩容。当容器的长度大于8时,是7/8扩容。也就是说当容器的长度为8时,容器可以存储8个方法。当容器的长度为16时,当第15个方法需要存储进来的时候,容器就要扩容了。

在x86_64架构下,刚开始初始化的容器的长度为4,是3/4扩容。这里的3/4扩容指的是:如果容器的长度为4,当第3个数据需要存储的时候,就要扩容了。如果容器的长度为8,当第6个数据需要存储的时候,就要扩容了。也就是说容器只能存储容器长度的3/4减1个方法。

还有一点就是:当容器扩容之后,前面存储的方法也会随之清空。

参考链接:

iOS面试题:简单的描述一下 SDWebImage的缓存策略?

首先, SDWebImage 的图片缓存采用的是 Memory (内存) 和 Disk (硬盘) 双重 Cache 机制, SDImageCache 中有一个叫做 memCache 的属性,它是一个 NSCache 对象,用于实现我们对图片的 Memory Cache ,其实就是接受系统的内存警告通知,然后清除掉自身的图片缓存。 Disk Cache ,也就是文件缓存, SDWebImage 会将图片存放到 NSCachesDirectory 目录中,然后为每一个缓存文件生成一个 md5 文件名, 存放到文件中。 整体机制如下:

原文地址

标题名称:ios开发缓存策略,ios缓存机制
本文链接:https://www.cdcxhl.com/article28/dsggojp.html

成都网站建设公司_创新互联,为您提供虚拟主机网站建设定制开发小程序开发网站维护企业网站制作

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

营销型网站建设