iOS 缓存,以及缓存大小,缓存清理等

AFNetWorking 在IOS开发中是一个经常会用的第三方开源库,其最好处是维护及时,源码开源。 

常用GET与POST请求方法:

POST请求: 

//初始化一个请求对象 
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
  NSString * url = @"你的请求地址";
  //dic 为参数字典
 [manager POST:url parameters:dic success:^(AFHTTPRequestOperation *operation, id responseObject) {
    //请求成功的回调
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    //请求失败的回调    
    }];

GET请求: 

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
 NSString * url = @"你的请求地址";
   [manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    //请求成功的回调
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    //请求失败的回调   
    }];

这里有一个地方需要注意, 

[AFHTTPRequestOperationManager manager]

这个类方法我们点进源码可以发现: 

+ (instancetype)manager {
    return [[self alloc] initWithBaseURL:nil];
}

这里初始化了一个返回了一个新的对象,并不是单例。 

使用这样的下载方法,下载完成后的数据AFNetWorking会帮我们自动解析,但是有时候服务器给的数据并不标准,这时我们需要加上这个设置: 


manager.responseSerializer = [AFHTTPResponseSerializer serializer]; 

这样我们将得到原始的HTTP返回给我们数据。 

我们再来探究一下,下载成功后,回调方法里的参数到底是什么东西 


success:^(AFHTTPRequestOperation *operation, id responseObject)

其中,第二个参数 responseObject 是下载下来的data数据,可直接进行JSON等解析。 

第一个参数,是个AFHTTPRequestOperation对象,来看源文件

@interface AFHTTPRequestOperation : AFURLConnectionOperation

@property (readonly, nonatomic, strong) NSHTTPURLResponse *response;

@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;

@property (readonly, nonatomic, strong) id responseObject;

@end

可以发现,里面有一个成员便是responseObject,同时,AFHTTPRequestOperation是继承于AFURLConnectionOperation,我们在看看AFURLConnectionOperation这个类: 

@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate, NSURLConnectionDataDelegate, NSSecureCoding, NSCopying>

@property (nonatomic, strong) NSSet *runLoopModes;

@property (readonly, nonatomic, strong) NSURLRequest *request;

@property (readonly, nonatomic, strong) NSURLResponse *response;

@property (readonly, nonatomic, strong) NSError *error;

@property (readonly, nonatomic, strong) NSData *responseData;

@property (readonly, nonatomic, copy) NSString *responseString;

@property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding;

@property (nonatomic, assign) BOOL shouldUseCredentialStorage;

@property (nonatomic, strong) NSURLCredential *credential;

@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

@property (nonatomic, strong) NSInputStream *inputStream;

@property (nonatomic, strong) NSOutputStream *outputStream;

@property (nonatomic, strong) dispatch_queue_t completionQueue;

@property (nonatomic, strong) dispatch_group_t completionGroup;

@property (nonatomic, strong) NSDictionary *userInfo;

- (instancetype)initWithRequest:(NSURLRequest *)urlRequest NS_DESIGNATED_INITIALIZER;

- (void)pause;

- (BOOL)isPaused;

- (void)resume;

看到这里,就离AFNETWorking封装的源头很近了,里面的成员非常多,其中包含了大部分我们需要的信息,可以通过点语法取到,其中有输入输出流,错误信息,请求到的Data数据,以及请求到的字符串数据  

responseString

我们可以通过 

NSLog ( @"operation: %@" , operation. responseString );

来打印查看请求到的原始信息。 

几点注意: 

1.关于崩溃url为nil 

大多数这样的原因是url中有特殊字符或者中文字符,AFNETWorking并没有做UTF8的转码,需要: 

url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

2.添加HttpHead字段的方法 

 //为这个下载任务HTTP头添加@"User-Agent"字段
 [manager.requestSerializer setValue:_scrData forHTTPHeaderField:@"User-Agent"];
 //打印头信息
    NSLog(@"%@",manager.requestSerializer.HTTPRequestHeaders);

在下载请求中,经常会请求一些不长变化的数据,如果每次APP启动都进行请求,会消耗许多资源,并且有时候缓存的处理,可以大大改善用户体验。 

在AFNETWorking中,并没有提供现成的缓存方案,我们可以通过写文件的方式,自行做缓存。 

在下载方法中: 

[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        //写缓存
        NSString *cachePath = @"你的缓存路径";//  /Library/Caches/MyCache
        [data writeToFile:cachePath atomically:YES];
                succsee(data);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    }];

然后在每次下载前,进行如下判断: 

 NSString * cachePath = @"你的缓存路径";
        if ([[NSFileManager defaultManager] fileExistsAtPath:cachePath]) {
            //从本地读缓存文件
            NSData *data = [NSData dataWithContentsOfFile:cachePath];
            }

有时,我们的下载请求可能是用户的动作触发的,比如一个按钮。我们还应该做一个保护机制的处理, 

//初始化一个下载请求数组
NSArray * requestArray=[[NSMutableArray alloc]init];
//每次开始下载任务前做如下判断
for (NSString * request in requestArray) {
        if ([url isEqualToString:request]) {
            return;
        }
    }
 [requestArray addObject:url];
 //下载成功或失败后
 [manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        [requestArray removeObject:url]
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        [requestArray removeObject:url]
    }];

至此,一个比较完成AFNETWorking请求使用流程就完成了。




//获取缓存文件路径

-(NSString *)getCachesPath{

    // 获取Caches目录路径

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);

    NSString *cachesDir = [paths objectAtIndex:0];

    

    NSString *filePath = [cachesDir stringByAppendingPathComponent:@"com.nickcheng.NCMusicEngine"];

    

    return filePath;

}

///计算缓存文件的大小的M

- (long long) fileSizeAtPath:(NSString*) filePath{

    NSFileManager* manager = [NSFileManager defaultManager];

    if ([manager fileExistsAtPath:filePath]){

        

//        //取得一个目录下得所有文件名

//        NSArray *files = [manager subpathsAtPath:filePath];

//        NSLog(@"files1111111%@ == %ld",files,files.count);

//        

//        // 从路径中获得完整的文件名(带后缀)

//        NSString *exe = [filePath lastPathComponent];

//        NSLog(@"exeexe ====%@",exe);

//        

//        // 获得文件名(不带后缀)

//        exe = [exe stringByDeletingPathExtension];

//        

//        // 获得文件名(不带后缀)

//        NSString *exestr = [[files objectAtIndex:1] stringByDeletingPathExtension];

//        NSLog(@"files2222222%@  ==== %@",[files objectAtIndex:1],exestr);

        

        

        return [[manager attributesOfItemAtPath:filePath error:nil] fileSize];

    }

    

    return 0;

}

- (float ) folderSizeAtPath:(NSString*) folderPath{

    NSFileManager* manager = [NSFileManager defaultManager];

    if (![manager fileExistsAtPath:folderPath]) return 0;

    NSEnumerator *childFilesEnumerator = [[manager subpathsAtPath:folderPath] objectEnumerator];//从前向后枚举器//////

    NSString* fileName;

    long long folderSize = 0;

    while ((fileName = [childFilesEnumerator nextObject]) != nil){

        NSLog(@"fileName ==== %@",fileName);

        NSString* fileAbsolutePath = [folderPath stringByAppendingPathComponent:fileName];

        NSLog(@"fileAbsolutePath ==== %@",fileAbsolutePath);

        folderSize += [self fileSizeAtPath:fileAbsolutePath];

    }

    NSLog(@"folderSize ==== %lld",folderSize);

    return folderSize/(1024.0*1024.0);

}

-(void)ss{

    // 获取Caches目录路径

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);

    NSString *cachesDir = [paths objectAtIndex:0];

    

    NSLog(@"cachesDircachesDir == %@",cachesDir);

    //读取缓存里面的具体单个文件/或全部文件//

    NSString *filePath = [cachesDir stringByAppendingPathComponent:@"com.nickcheng.NCMusicEngine"];

    NSArray *array = [[NSArray alloc]initWithContentsOfFile:filePath];

    NSLog(@"filePathfilePath%@ ==array==== %@",filePath, array);

    

    

    NSFileManager* fm=[NSFileManager defaultManager];

    if([fm fileExistsAtPath:filePath]){

        //取得一个目录下得所有文件名

        NSArray *files = [fm subpathsAtPath:filePath];

        NSLog(@"files1111111%@ == %ld",files,files.count);

        

        // 获得文件名(不带后缀)

        NSString * exestr = [[files objectAtIndex:1] stringByDeletingPathExtension];

        NSLog(@"files2222222%@  ==== %@",[files objectAtIndex:1],exestr);

    }


}

Why Cache

有时候,对同一个URL请求多次,返回的数据可能都是一样的,比如服务器上的某张图片,无论下载多少次,返回的数据都是一样的。

这里写图片描述

上面的情况会造成以下问题

(1)用户流量的浪费
(2)程序响应速度不够快

解决上面的问题,一般考虑对数据进行缓存。

数据缓存

为了提高程序的响应速度,可以考虑使用缓存(内存缓存\硬盘缓存)r

这里写图片描述

第一次请求数据时,内存缓存中没有数据,硬盘缓存中没有数据。

缓存数据的过程:

这里写图片描述

?
1
2
3
4
5
6
7
8
9
10
11
<code>? 当服务器返回数据时,需要做以下步骤
1 )使用服务器的数据(比如解析、显示)
2 )将服务器的数据缓存到硬盘(沙盒)
  此时缓存的情况是:内存缓存中有数据,硬盘缓存中有数据。
 
? 再次请求数据分为两种情况:
1 )如果程序并没有被关闭,一直在运行
       请求数据-> 内存数据
2 )如果程序重新启动
     请求数据->硬盘数据-> 再次请求数据-> 内存数据
</code>

提示:数据从硬盘读入内存-> 程序开启-> 内存中一直有数据

缓存的实现

说明:

?
1
2
3
4
5
6
7
<code>? 由于GET请求一般用来查询数据
? POST请求一般是发大量数据给服务器处理(变动性比较大)
=>因此一般只对GET请求进行缓存,而不对POST请求进行缓存
 
? 在iOS中,可以使用NSURLCache类缓存数据
? iOS 5 之前:只支持内存缓存。从iOS 5 开始:同时支持内存缓存和硬盘缓存
</code>

NSURLCache

?
1
2
3
4
<code>iOS中得缓存技术用到了NSURLCache类。
? 缓存原理:一个NSURLRequest对应一个NSCachedURLResponse
? 缓存技术:把缓存的数据都保存到数据库中。
</code>

NSURLCache的常见用法

(1)获得全局缓存对象(没必要手动创建)

?
1
<code class = "hljs fix" >    NSURLCache *cache = [NSURLCache sharedURLCache]; </code>

(2)设置内存缓存的最大容量(字节为单位,默认为512KB)

?
1
<code class = "hljs bash" >    - ( void )setMemoryCapacity:(NSUInteger)memoryCapacity;</code>

(3)设置硬盘缓存的最大容量(字节为单位,默认为10M)

?
1
<code class = "hljs erlang" >- ( void )setDiskCapacity:(NSUInteger)diskCapacity;</code>

(4)硬盘缓存的位置:

?
1
<code class = "hljs vhdl" >    沙盒/Library/Caches</code>

(5)取得某个请求的缓存

?
1
<code class = "hljs erlang" >- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request; </code>

(6)清除某个请求的缓存

?
1
<code class = "hljs objectivec" >    - ( void )removeCachedResponseForRequest:(NSURLRequest *)request;</code>

(7)清除所有的缓存

?
1
<code class = "hljs erlang" >- ( void )removeAllCachedResponses;</code>

缓存GET请求

?
1
2
<code>要想对某个GET请求进行数据缓存,非常简单
</code>
?
1
2
3
4
<code class = "hljs fix" >NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
 
// 设置缓存策略
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;</code>

只要设置了缓存策略,系统会自动利用NSURLCache进行数据缓存

7种缓存策略

?
1
2
3
4
5
6
7
8
<code>? NSURLRequestUseProtocolCachePolicy // 默认的缓存策略(取决于协议)
 
? NSURLRequestReloadIgnoringLocalCacheData // 忽略缓存,重新请求
 
? NSURLRequestReturnCacheDataElseLoad // 有缓存就用缓存,没有缓存就重新请求 
 
? NSURLRequestReturnCacheDataDontLoad // 有缓存就用缓存,没有缓存就不发请求,当做请求出错处理(用于离线模式)
</code>

NSURLRequestReloadIgnoringLocalAndRemoteCacheData // 未实现
NSURLRequestReloadRevalidatingCacheData // 未实现
NSURLRequestReloadIgnoringLocalAndRemoteCacheData // 未实现

缓存的注意事项

缓存的设置需要根据具体的情况考虑,如果请求某个URL的返回数据:

?
1
2
3
4
<code>    ( 1 )经常更新:不能用缓存!比如股票、彩票数据
     2 )一成不变:果断用缓存
     3 )偶尔更新:可以定期更改缓存策略 或者 清除缓存
</code>

提示:如果大量使用缓存,会越积越大,建议定期清除缓存

GET缓存

在appDelegate中设置网络缓存大小 实现get 缓存:
如果从服务器加载数据,通过etag 判断加载数据与缓存是否相同 从本地加载数据
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<code class = "hljs java" >    //
     //  AppDelegate.m
     //  A-get缓存请求
     //
     //  Created by Mr.Sunday on 15/4/27.
     //  Copyright (c) 2015年 Novogene. All rights reserved.
     //
 
     # import "AppDelegate.h"
 
     @interface AppDelegate ()
 
     @end
 
     @implementation AppDelegate
     - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
     {
 
         /*===============设置网络缓存==============*/
         /**
          内存缓存->4M
          磁盘缓存->20M
          diskPath 如果是 nil,会缓存到 cached 的 bundleId 目录下
 
          只要在 AppDelegate 中加入以下两句话,今后所有的缓存处理,就不需要管了!
          */
 
         NSURLCache *cathe = [[NSURLCache alloc] initWithMemoryCapacity: 4 * 1024 * 1024 diskCapacity: 20 * 1024 * 1024 diskPath:nil];
         [NSURLCache setSharedURLCache:cathe];
 
         /**
          SDWebImage 的缓存
          1. 缓存时间:1周
          2. 处理缓存文件,监听系统退出到后台的事件
          - 遍历缓存文件夹,删除所有过期的文件
          - 继续遍历缓存文件夹,将最大的文件删除,一直删除到缓存文件的大小和指定的“磁盘限额”一致,停止
          */
         return YES;
     }
</code>
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<code class = "hljs java" >    //
     //  ViewController.m
     //  A-get缓存请求
     //
     //  Created by Mr.Sunday on 15/4/27.
     //  Copyright (c) 2015年 Novogene. All rights reserved.
     //
 
     # import "ViewController.h"
 
     @interface ViewController ()
 
     @property (nonatomic, weak) IBOutlet UIImageView *iconView;
     //服务器返回的etag
     @property (nonatomic, copy) NSString *etag;
     //清楚所有缓存
     - ( void )removeAllCachedResponses;
 
     @end
 
     @implementation ViewController
 
     - ( void )viewDidLoad
     {
         [ super viewDidLoad];
     }
 
     /**
      1. 请求的缓存策略使用 >NSURLRequestReloadIgnoringCacheData<,忽略本地缓存
      2. 服务器响应结束后,要记录 Etag,服务器内容和本地缓存对比是否变化的重要依据!
      3. 在发送请求时,设置 If-None-Match,并且传入 etag
      4. 连接结束后,要判断响应头的状态码,如果是 304,说明本地缓存内容没有发生变化
      */
 
     /*==================从本地缓存加载数据==============*/
     /*
      NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
      self.iconView.image = [UIImage imageWithData:cachedResponse.data];
      */
     - ( void )touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
     {
         NSURL *url = [NSURL URLWithString:@ "http://mrsunday.local/ml.png" ];
 
         NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval: 10.0 ];
 
         /**
          *设置请求头->所有的请求头都是通过这种方法设置的
          *如果etag length不为0,说明已经有缓存了
          */
         if (self.etag.length > 0 )
         {
             NSLog(@ "设置 etag: %@" , self.etag);
             [request setValue:self.etag forHTTPHeaderField:@ "IF-None-Match" ];
         }
 
         //请求的默认方法是get(高频使用)
         NSLog(@ "%@" ,request.HTTPMethod);
 
         /**
          *Etag = "\"4a0b9-514a2d804bd40\"";
          *可以在请求中增加一个 etag 跟服务器返回的 etag 进行对比
          *就能够判断服务器对应的资源是否发生变化,具体更新的时间,由request自行处理
          */
 
         [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
         {
             NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
             /**
              *allHeaderFields 所有相应头子端
              */
             NSLog(@ "%@ %@" , httpResponse.allHeaderFields, httpResponse);
 
             /**
              *如果服务器的状态码是304,说明数据已经被缓存,服务器不再需要返回数据
              *需要从本地缓存获取被缓存的数据
              */
             if (httpResponse.statusCode == 304 )
             {
                 NSLog(@ "load local database" );
 
                 /**
                  *针对http访问的一个缓存类,提供了一个单例
                  *拿到被缓存的响应
                  */
                 NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
                 self.iconView.image = [UIImage imageWithData:cachedResponse.data];
 
                 return ;
             }
 
             //记录etag
             self.etag = httpResponse.allHeaderFields[@ "etag" ];
             self.iconView.image = [UIImage imageWithData:data];
         }];
     }
 
     //记得要清除缓存请求!
     - ( void )removeAllCachedResponses
     {
         [self removeAllCachedResponses];
     }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值