iOS中如何自訂加密緩衝及緩衝原理分析(第一節:緩衝原理分析)
最近一個朋友問我如何自己做一個圖片緩衝功能,說實話之前還真的沒有好好研究下,到底是如何在項目中做緩衝的?以及如果需要更加機密的緩衝如何做呢?正好項目中做了不錯的緩衝功能,我就拋磚引玉了,大神請見諒,本人眼界有限,求拍磚。
首先明白一點,OC是存在一個緩衝類的,關鍵我們是如何使用它的。
@interface NSCache : NSObject {
- (id)objectForKey:(id)key;
- (void)setObject:(id)obj forKey:(id)key; // 0 cost
上面的兩個紅色的方法,就告訴我們使用的方法就是通過類似設定字典索引值對的形勢來儲存資料的。所以可以簡化理解為緩衝類就是一個自訂字典。我們要緩衝的話,只需要把這個字典做成一個在在app所有頁面都唯一存在的字典就好了,所以需要把它設定為一個單例類,什麼是單例類大家自己去查查。
還有個一個點大家可能沒有考慮到,就是我們的沙箱中明明也存在一個叫做cache的檔案夾,這個就是儲存快取資料的檔案夾,所以我們要聯合起來用。
如何聯合來用?
大致思路就是我們儲存到緩衝單例類的同時也儲存在沙箱緩衝(儲存到沙箱緩衝中記得在主線程中執行)中,擷取的時候首先去擷取緩衝類中的快取資料,如果不存在在去沙箱快取檔案夾下去擷取,最後再找不到才算不存在快取資料。
說了一大通我們先看看AFNetworking源碼是如何緩衝圖片的。
// UIImageView+AFNetworking.m//// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com)//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to deal// in the Software without restriction, including without limitation the rights// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell// copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN// THE SOFTWARE.#import "UIImageView+AFNetworking.h"#import #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)#import "AFHTTPRequestOperation.h"
//緩衝類@interface AFImageCache : NSCache @end#pragma mark -
//UIImageview類別@interface UIImageView (_AFNetworking)@property (readwrite, nonatomic, strong, setter = af_setImageRequestOperation:) AFHTTPRequestOperation *af_imageRequestOperation;@end@implementation UIImageView (_AFNetworking)+ (NSOperationQueue *)af_sharedImageRequestOperationQueue { static NSOperationQueue *_af_sharedImageRequestOperationQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _af_sharedImageRequestOperationQueue = [[NSOperationQueue alloc] init]; _af_sharedImageRequestOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; }); return _af_sharedImageRequestOperationQueue;}- (AFHTTPRequestOperation *)af_imageRequestOperation { return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_imageRequestOperation));}- (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation { objc_setAssociatedObject(self, @selector(af_imageRequestOperation), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}@end#pragma mark -
//圖片類別@implementation UIImageView (AFNetworking)@dynamic imageResponseSerializer;+ (id )sharedImageCache { static AFImageCache *_af_defaultImageCache = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _af_defaultImageCache = [[AFImageCache alloc] init]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * __unused notification) { [_af_defaultImageCache removeAllObjects]; }]; });#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wgnu" return objc_getAssociatedObject(self, @selector(sharedImageCache)) ?: _af_defaultImageCache;
// 注意這一句,意思就是如果我們自訂cache有的話,就直接返回我們自訂的cache,沒有就返回系統內部的AFImageCache,一旦手動設定之後我們以後擷取的時候就返回自訂的
// 有些同學可能認為這個我們最初設定了確實存在,那下次再訪問不就沒了,注意這個objc_getAssociatedObject,類似於系統userdefault一旦設定了,除非你清理掉不然一直有
#pragma clang diagnostic pop}+ (void)setSharedImageCache:(id)imageCache { objc_setAssociatedObject(self, @selector(sharedImageCache), imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (id )imageResponseSerializer { static id _af_defaultImageResponseSerializer = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _af_defaultImageResponseSerializer = [AFImageResponseSerializer serializer]; });#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wgnu" return objc_getAssociatedObject(self, @selector(imageResponseSerializer)) ?: _af_defaultImageResponseSerializer;#pragma clang diagnostic pop}- (void)setImageResponseSerializer:(id )serializer { objc_setAssociatedObject(self, @selector(imageResponseSerializer), serializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}#pragma mark -//外部供我們使用的介面- (void)setImageWithURL:(NSURL *)url { [self setImageWithURL:url placeholderImage:nil];}
cept屬
//內部實現第一步,添加一個accept屬性(規定通過能夠通過檔案上傳進行提交的檔案類型image類型)- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage{ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];}
//內部實現的關鍵,主要原理就是判斷單例緩衝類裡面是否有此url對應的圖片(注意內部實現就是擷取cache中,沒有就真沒有了),如果存在就直接返給外部,沒有的話,就通過url請求去擷取,擷取到了之後再存入緩衝(存入cache)- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure{ [self cancelImageRequestOperation]; UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; //擷取 if (cachedImage) { if (success) { success(nil, nil, cachedImage); } else { self.image = cachedImage; } self.af_imageRequestOperation = nil; } else { if (placeholderImage) { self.image = placeholderImage; } __weak __typeof(self)weakSelf = self; self.af_imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; self.af_imageRequestOperation.responseSerializer = self.imageResponseSerializer; [self.af_imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) { if (success) { success(urlRequest, operation.response, responseObject); } else if (responseObject) { strongSelf.image = responseObject; } if (operation == strongSelf.af_imageRequestOperation){ strongSelf.af_imageRequestOperation = nil; } } [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; //存入 } failure:^(AFHTTPRequestOperation *operation, NSError *error) { __strong __typeof(weakSelf)strongSelf = weakSelf; if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) { if (failure) { failure(urlRequest, operation.response, error); } if (operation == strongSelf.af_imageRequestOperation){ strongSelf.af_imageRequestOperation = nil; } } }]; [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_imageRequestOperation]; }}- (void)cancelImageRequestOperation { [self.af_imageRequestOperation cancel]; self.af_imageRequestOperation = nil;}@end#pragma mark -static inline NSString * AFImageCacheKeyFromURLRequest(NSURLRequest *request) { return [[request URL] absoluteString];}@implementation AFImageCache
//緩衝實現就是通過下面兩個方法來實現存取的- (UIImage *)cachedImageForRequest:(NSURLRequest *)request { switch ([request cachePolicy]) { case NSURLRequestReloadIgnoringCacheData: case NSURLRequestReloadIgnoringLocalAndRemoteCacheData: return nil; default: break; }return [self objectForKey:AFImageCacheKeyFromURLRequest(request)];}- (void)cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request{ if (image && request) { [self setObject:image forKey:AFImageCacheKeyFromURLRequest(request)]; }}@end#endif
上面的緩衝是AF內建的