iOS NSCache和NSUrlCache緩存類實(shí)現(xiàn)示例詳解
NSCache
NSCache是Foundation框架提供的緩存類的實(shí)現(xiàn),使用方式類似于可變字典,最重要的是它是線程安全的,而NSMutableDictionary不是線程安全的,在多線程環(huán)境下使用NSCache是更好的選擇。 類的基本屬性和方法:
#import <Foundation/NSObject.h> @class NSString; @protocol NSCacheDelegate; NS_ASSUME_NONNULL_BEGIN API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) @interface NSCache <KeyType, ObjectType> : NSObject { @private id _delegate; void *_private[5]; void *_reserved; } @property (copy) NSString *name; @property (nullable, assign) id<NSCacheDelegate> delegate; - (nullable ObjectType)objectForKey:(KeyType)key; - (void)setObject:(ObjectType)obj forKey:(KeyType)key; // 0 cost - (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g; - (void)removeObjectForKey:(KeyType)key; - (void)removeAllObjects; @property NSUInteger totalCostLimit; // limits are imprecise/not strict @property NSUInteger countLimit; // limits are imprecise/not strict @property BOOL evictsObjectsWithDiscardedContent; @end @protocol NSCacheDelegate <NSObject> @optional - (void)cache:(NSCache *)cache willEvictObject:(id)obj; @end NS_ASSUME_NONNULL_END
緩存淘汰策略
通過 GNUstep 提供的源碼,我們得知其對于 NSCache 的處理是計(jì)算出一個平均訪問次數(shù),然后釋放的是訪問次數(shù)較少的對象,直到滿足需要釋放大小,也就是 LRU 的機(jī)制。通過 swift-corelibs-foundation 源碼我們得知,其首先存儲鏈表結(jié)構(gòu)中是按對象花費(fèi)內(nèi)存大小排序的,然后通過用戶有無指定 totalCostLimit 大小限制來依次釋放(先釋放占用較小的對象),直到滿足需要釋放大小。然后再通過個數(shù)限制來釋放,直到滿足需要釋放大小(依舊是先釋放較小的對象)。
GNUstepFoundation 源碼地址:github.com/gnustep/lib…
Swift Foundation 源碼地址:github.com/apple/swift…
在內(nèi)存不足時NSCache會自動釋放存儲的對象。
NSCache的鍵key不會被復(fù)制,所以key不需要實(shí)現(xiàn)NSCopying協(xié)議。
可以設(shè)置緩存的最大數(shù)量,當(dāng)緩存數(shù)量滿的時候,再添加將先刪除先添加的對象再增加。
唯一一個代理方法是一個對象將被刪除時調(diào)用,調(diào)用方式有以下幾種:
- NSCache緩存對象自身被釋放
- 手動調(diào)用removeObjectForKey:方法
- 手動調(diào)用removeAllObjects
- 緩存中對象的個數(shù)大于countLimit,或,緩存中對象的總cost值大于totalCostLimit
- 程序進(jìn)入后臺后
- 收到系統(tǒng)的內(nèi)存警告
基本用法:
@interface NSCacheViewController ()<NSCacheDelegate> @property(nonatomic,strong) NSCache *cache; @end @implementation NSCacheViewController - (void)viewDidLoad { [super viewDidLoad]; self.cache = [[NSCache alloc] init]; //設(shè)置最大緩存數(shù) self.cache.countLimit = 5; //設(shè)置代理,實(shí)現(xiàn)代理方法, self.cache.delegate = self; self.cache.name = @"測試內(nèi)存"; for (int i=0; i<10; i++) { NSString *str = [NSString stringWithFormat:@"%d",i]; [self.cache setObject:str forKey:str]; } // Do any additional setup after loading the view. } //超出緩存部分會被釋放, 收到內(nèi)存警告時候系統(tǒng)也會釋放一部分。 -(void)cache:(NSCache *)cache willEvictObject:(id)obj{ NSLog(@"%@---%@",cache,obj); } @end
實(shí)際應(yīng)用 SDWebImage
SDImageCacheConfig中可以配置是否使用內(nèi)存做緩存,默認(rèn)為YES
磁盤緩存的最大時長,默認(rèn)為一周
SDImage中內(nèi)存緩存SDMemoryCache繼承與NScache,緩存時候會在NSCache和SDMemoryCache的NSMapTable各存一份。讀取時候也優(yōu)先讀取系統(tǒng)cache,如果不存在再讀取SDMemoryCache,這樣做的目標(biāo)是防止一些系統(tǒng)緩存不可控因素。
// `setObject:forKey:` just call this with 0 cost. Override this is enough - (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g { [super setObject:obj forKey:key cost:g]; if (!self.config.shouldUseWeakMemoryCache) { return; } if (key && obj) { // Store weak cache SD_LOCK(_weakCacheLock); [self.weakCache setObject:obj forKey:key]; SD_UNLOCK(_weakCacheLock); } }
NSURLCache
使用緩存的目的是為了使應(yīng)用程序能更快速的響應(yīng)用戶輸入,是程序高效的運(yùn)行。有時候我們需要將遠(yuǎn)程web服務(wù)器獲取的數(shù)據(jù)緩存起來,以空間換取時間,減少對同一個url多次請求,減輕服務(wù)器的壓力,優(yōu)化客戶端網(wǎng)絡(luò),讓用戶體驗(yàn)更良好。
背景:NSURLCache : 在iOS5以前,apple不支持磁盤緩存,在iOS5的時候,允許磁盤緩存,(NSURLCache 是根據(jù)NSURLRequest 來實(shí)現(xiàn)的)只支持http,在iOS6以后,支持http和https。
緩存的實(shí)現(xiàn)說明:由于GET請求一般用來查詢數(shù)據(jù),POST請求一般是發(fā)大量數(shù)據(jù)給服務(wù)器處理(變動性比較大),因此一般只對GET請求進(jìn)行緩存,而不對POST請求進(jìn)行緩存。
緩存原理:一個NSURLRequest對應(yīng)一個NSCachedURLResponse
Etag全稱是Entity Tag,一般用于標(biāo)識URL對象是否發(fā)生了改變。 使用Etag一般會出現(xiàn)如下的請求流程:
Etag有點(diǎn)類似于文件hash或者說是信息摘要。
當(dāng)進(jìn)行一次URL請求,服務(wù)端會返回'Etag'響應(yīng)頭,下次瀏覽器請求相同的URL時,瀏覽器會自動將它設(shè)置為請求頭'If-None-Match'的值。服務(wù)器收到這個請求之后,就開始做信息校驗(yàn)工作將自己本次產(chǎn)生的Etag與請求傳遞過來的'If-None-Match'對比,如果相同,則返回HTTP狀態(tài)碼304,并且response數(shù)據(jù)體中沒有數(shù)據(jù)。
第二次請求的時候從哪里獲取到'Etag'的值并賦給請求頭'If-None-Match'的?自然是瀏覽器的緩存中取出的。那么瀏覽器收到304狀態(tài)碼之后又干了什么?剛才說到response數(shù)據(jù)體中沒有數(shù)據(jù),但是瀏覽器仍需加載頁面,它會從緩存中讀取上次緩存的頁面。
// // NSURLCacheViewController.m // DemoTest2022 // // Created by wy on 2022/10/19. // #import "NSURLCacheViewController.h" @interface NSURLCacheViewController () //去服務(wù)器比對資源是否需要更新 @property (nonatomic, strong) NSString *lastModified; @property (nonatomic, strong) NSString *etag; @property(nonatomic,strong) UIImageView *imagev; @end @implementation NSURLCacheViewController - (void)viewDidLoad { [super viewDidLoad]; self.imagev = [[UIImageView alloc] initWithFrame:CGRectMake(0, 100, 100, 100)]; [self.view addSubview:self.imagev]; // Do any additional setup after loading the view. } -(void)requestTest{ NSURL *url = [NSURL URLWithString:@"http://via.placeholder.com/50x50.png"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:(NSURLRequestReloadIgnoringCacheData) timeoutInterval:15] ; if (self.lastModified) { [request setValue:self.lastModified forHTTPHeaderField:@"If-Modified-Since"]; } if (self.etag) { [request setValue:self.etag forHTTPHeaderField:@"If-None-Match"]; } [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { NSLog(@"error---%@",error); }else{ NSData *tempData = data; NSString *responseStr = [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding]; self.lastModified = [(NSHTTPURLResponse *)response allHeaderFields][@"Last-Modified"]; self.etag =[(NSHTTPURLResponse *)response allHeaderFields][@"etag"]; NSLog(@"response:%@", response); dispatch_sync(dispatch_get_main_queue(), ^{ self.imagev.image = [UIImage imageWithData:tempData]; }); } }] resume] ; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self requestTest]; } @end
這樣每次請求都使用忽略緩存的策略,但是要附帶著"If-None-Match"頭,它的值是上次請求的響應(yīng)頭"Etag"的值,于是服務(wù)器會每次都實(shí)時檢查文件的修改狀態(tài),得到一個準(zhǔn)確的狀態(tài)值,最后決定返回304還是200。若是200,iOS則直接使用新的response和新的數(shù)據(jù);如果是304,則使用新的response和緩存中的data。
這樣既能夠獲取到最新的數(shù)據(jù)有能夠節(jié)約帶寬。兩全其美。
iOS中定以的URLRequest緩存策略有以下幾種:
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy) { NSURLRequestUseProtocolCachePolicy = 0, // 從不讀取緩存,但請求后將response緩存起來 NSURLRequestReloadIgnoringLocalCacheData = 1, NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData, // 以下兩種在取緩存時,可能取到的是過期數(shù)據(jù) NSURLRequestReturnCacheDataElseLoad = 2,// 緩存中沒有才去發(fā)起請求加載,有就不進(jìn)行網(wǎng)絡(luò)請求了 NSURLRequestReturnCacheDataDontLoad = 3,// 緩存中沒有不加載,絕不發(fā)起網(wǎng)絡(luò)請求,緩存中沒有則返回錯誤 NSURLRequestReloadRevalidatingCacheData = 5,//Unimplemented };
官方文檔解釋:
NSURLCache類通過將NSURLRequest對象映射到NSCachedURLResponse對象來實(shí)現(xiàn)URL加載請求響應(yīng)的緩存。它提供了一個復(fù)合的內(nèi)存和磁盤緩存,并允許您操作內(nèi)存和磁盤部分的大小。您還可以控制緩存數(shù)據(jù)持久存儲的路徑。
在iOS中,當(dāng)系統(tǒng)磁盤空間不足時,磁盤上的緩存可能會被清除,但只有在應(yīng)用程序未運(yùn)行時才會清除。
AFNetwork中用法:
總結(jié):
NSCache 特點(diǎn)
- 使用方便,類似字典
- l線程安全
- l內(nèi)存不足時,NSCache會自動釋放存儲對象
- NSCache的key不會被拷貝,不需要實(shí)現(xiàn)NSCopying協(xié)議
- lNSDiscardableContent協(xié)議
NSURLCache主要應(yīng)用與網(wǎng)絡(luò)請求數(shù)據(jù)緩存策略,優(yōu)化網(wǎng)絡(luò)請求性能優(yōu)化。
以上就是iOS NSCache和NSUrlCache緩存類實(shí)現(xiàn)示例詳解的詳細(xì)內(nèi)容,更多關(guān)于iOS NSCache NSUrlCache的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS應(yīng)用運(yùn)用設(shè)計(jì)模式中的Strategy策略模式的開發(fā)實(shí)例
這篇文章主要介紹了iOS應(yīng)用開發(fā)中對設(shè)計(jì)模式中的Strategy策略模式的運(yùn)用,例子采用傳統(tǒng)的Objective-C語言代碼演示,需要的朋友可以參考下2016-03-03iOS中時間與時間戳的相互轉(zhuǎn)化實(shí)例代碼
這篇文章主要介紹了iOS中時間與時間戳的相互轉(zhuǎn)化實(shí)例代碼,非常具有實(shí)用價值,需要的朋友可以參考下。2017-03-03Xcode 下刪除Provisioning Profiles文件詳細(xì)介紹
這篇文章主要介紹了Xcode 下刪除Provisioning Profiles文件詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-12-12iOS開發(fā)中實(shí)現(xiàn)郵件和短信發(fā)送的簡單示例
這篇文章主要介紹了iOS開發(fā)中實(shí)現(xiàn)郵件和短信發(fā)送的簡單示例,編程語言依然是傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-09-09iOS優(yōu)化UITableViewCell高度計(jì)算的一些事兒
這iOS開發(fā)中對于UITableViewCell高度自適應(yīng)的文章已經(jīng)很多很多,但都不是自己所需要的,下面篇文章主要給大家介紹了關(guān)于iOS優(yōu)化UITableViewCell高度計(jì)算的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-11-11IOS中UIWebView加載Loading的實(shí)現(xiàn)方法
最近有朋友問我類似微信語音播放的喇叭動畫和界面圖片加載loading界面是怎樣實(shí)現(xiàn)的,是不是就是一個gif圖片呢!我的回答當(dāng)然是否定了,當(dāng)然不排除也有人用gif圖片?。?/div> 2015-05-05最新評論