URLCache 总结


URLCache 

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        print( NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true))
        
        let urlCache = URLCache(memoryCapacity: 4 * 1024 * 1024, diskCapacity: 20 * 1024 * 1024, diskPath: nil)
        URLCache.shared  = urlCache  // 不赋值,也有默认的 URLCache
   
        return true
    }
1.  首先给  URLCache.shared 设置一个默认值
如果 不设置值那? 他会有一个默认的 urlCache ( memoryCapacity : 51200 B ,  diskCapacity : 10000000 B, diskPath:在默认的 cache下 ), 不信你可以打印出来看看。PS: memoryCapacity 和 diskCapacity 是可以改的 但 diskPath 不可以,所以diskPath 不需要改的会不需要重新创建一个URLCache
   2. URLCache 具体用法见我上一章

   3. URLRequest 中的  NSURLRequestCachePolicy
  • NSURLRequestUseProtocolCachePolicy: 对特定的 URL 请求使用网络协议中实现的缓存逻辑。这是默认的策略。
  • NSURLRequestReloadIgnoringLocalCacheData:数据需要从原始地址加载。不使用现有缓存。
  • NSURLRequestReloadIgnoringLocalAndRemoteCacheData:不仅忽略本地缓存,同时也忽略代理服务器或其他中间介质目前已有的、协议允许的缓存。
  • NSURLRequestReturnCacheDataElseLoad:无论缓存是否过期,先使用本地缓存数据。如果缓存中没有请求所对应的数据,那么从原始地址加载数据。
  • NSURLRequestReturnCacheDataDontLoad:无论缓存是否过期,先使用本地缓存数据。如果缓存中没有请求所对应的数据,那么放弃从原始地址加载数据,请求视为失败(即:“离线”模式)。
  • NSURLRequestReloadRevalidatingCacheData:从原始地址确认缓存数据的合法性后,缓存数据就可以使用,否则从原始地址加载。
HTTP / HTTPS 会自动缓存 除非在session delegate里 设置  CachedURLResponse storagePolicy: . notAllowed 如下方

/**

     向代理询问数据(或上传)任务是否应将响应存储在缓存中。

     在任务完成接收所有预期数据后,会话调用此委托方法。如果不实现此方法,则默认行为是使用会话配置对象中指定的缓存策略。此方法的主要目的是防止缓存特定的URL或修改与URL响应相关联的userInfo字典。

     仅当处理请求的URLProtocol决定缓存响应时才调用此方法。通常,只有当以下所有条件都为真时,才会缓存响应:

     请求是针对HTTP或HTTPS URL(或您自己的支持缓存的自定义网络协议)。

     请求已成功(状态码位于200-299范围内)。

     提供的响应来自服务器,而不是从缓存中。

     会话配置的缓存策略允许缓存。

     提供的NSURLRequest对象的缓存策略(如果适用)允许缓存。

     服务器响应中与缓存相关的头(如果存在)允许缓存。

     响应大小足够小以合理地适合缓存。 (例如,如果提供磁盘缓存,则响应不得大于磁盘缓存大小的大约5%)。

     **/

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) {

        

        let cacheResponse = CachedURLResponse(response: proposedResponse.response.copy() as! URLResponse, data: proposedResponse.data, userInfo: proposedResponse.userInfo, storagePolicy: .allowed)

        completionHandler(cacheResponse)

    }

)

我们着重看一下默认缓存策略UseProtocolCachePolicy和忽略缓存的策略ReloadIgnoringLocalCacheData,

当使用默认的缓存策略时:

第一次请求一个URL时,会将response和数据缓存起来,
再次请求相同的URL时,会使用缓存中的Etag作为这次请求的request的'If-None-Match'值,这样服务端会返回304并且response的数据体为空,此时iOS会帮助读取缓存中的数据体,修改次请求的response,将HTTP状态码改为200,使用修改后的response和缓存中取到的data作为参数执行完成回调。

以上过程看起来似乎很完美,除了状态码不是304,其他的过程和浏览器几乎一致。但是他有一个缺陷,在研究这个缺陷之前我们先弄清一个这么一个事实:请求内容可以分为三种 1.脚本2.用数据渲染的页面3.静态文件。

对于脚本请求的处理,服务端是会忽略Etag,而每次都会处理,这样返回的数据都是新的,返回HTTP状态码为200.
对于用数据渲染的页面,服务器会按照一定的计算规则,计算渲染之后的Etag,然后对比,再决定返回的是304或者200.
对于静态文件,有些服务器具有检测静态文件改变的能力,一旦文件发生改变,服务器会立刻检测到,从而返回200给客户端,而有些服务器检测文件改变的功能是有延迟的,或者根本没有这种功能,这样即使文件的内容改变了,服务器仍然认为没有改变,于是对比Etag依然相等,结果返回304.(这次测试使用了apache和Express,默认配置下的apache对文件改变的检测是有延迟的,Express则是实时检测的)

根据以上的描述就会暴露出使用默认缓存策略的一点劣势,如果服务器不能实时检测文件改变状态,那么文件是否改变的比对结果是不准确的。最糟糕的情况就是:当文件改变了,服务器认为仍然没有改变,从而返回了304,而没有携带最新的数据。

ReloadIgnoringLocalCacheData策略时:

每次请求前都会忽略缓存,request的header从来不会附带'If-None-Match'值, 服务器每次处理成功后都是返回200,这样每次都会拿到服务器的数据(每次response的Date头都是新的值),服务器返回的response带有完整的数据体。iOS接收到数据之后,将response和数据缓存,并作为参数执行完成回调。

这里我们也能够看到使用ReloadIgnoringLocalCacheData策略暴漏出来的缺点:尽管服务器端的文件确实没有改变,但iOS依然不使用本地已有的缓存,而每次服务端还要将数据发给客户端,这样是多么浪费带宽!



========= 验证 test ==========

let url = URL(string: "http://c.3g.163.com/photo/api/list/0096/4GJ60096.json")

        var request = URLRequest(url: url!)

        //request.cachePolicy = .useProtocolCachePolicy    // 默认缓存策略          test (没网的情况下)直接报错 、换句话说他要去向服务器请求资源是否改变的数据,而下方不需要 

        request.cachePolicy = .returnCacheDataElseLoad  // test (没网的情况下) 直接会去取缓存 statusCode 被改为 200

        let session = URLSession.shared //

        let dataTask = session.dataTask(with: request) {(data , response , error) in

            if let error = error {

                print("error + \(error.localizedDescription)")

                return

            }

            print("response state : \((response as! HTTPURLResponse).statusCode)")

            if let data = data {

                print(   String(data: data, encoding: .utf8) ?? "data null")

            }else{

                print("data is nil")

            }

        }

        dataTask.resume()


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值