版本記錄
版本號 | 時(shí)間 |
---|---|
V1.0 | 2018.03.06 |
前言
我們做APP發(fā)起網(wǎng)絡(luò)請求押搪,都離不開一個(gè)非常有用的框架AFNetworking,可以說這個(gè)框架的知名度已經(jīng)超過了蘋果的底層網(wǎng)絡(luò)請求部分艇劫,很多人可能不知道蘋果底層是如何發(fā)起網(wǎng)絡(luò)請求的掉缺,但是一定知道
AFNetworking
炼鞠,接下來幾篇我們就一起詳細(xì)的解析一下這個(gè)框架。感興趣的可以看上面寫的幾篇券躁。
1. AFNetworking源碼探究(一) —— 基本介紹
2. AFNetworking源碼探究(二) —— GET請求實(shí)現(xiàn)之NSURLSessionDataTask實(shí)例化(一)
3. AFNetworking源碼探究(三) —— GET請求實(shí)現(xiàn)之任務(wù)進(jìn)度設(shè)置和通知監(jiān)聽(一)
4. AFNetworking源碼探究(四) —— GET請求實(shí)現(xiàn)之代理轉(zhuǎn)發(fā)思想(一)
5. AFNetworking源碼探究(五) —— AFURLSessionManager中NSURLSessionDelegate詳細(xì)解析(一)
6. AFNetworking源碼探究(六) —— AFURLSessionManager中NSURLSessionTaskDelegate詳細(xì)解析(一)
7. AFNetworking源碼探究(七) —— AFURLSessionManager中NSURLSessionDataDelegate詳細(xì)解析(一)
8. AFNetworking源碼探究(八) —— AFURLSessionManager中NSURLSessionDownloadDelegate詳細(xì)解析(一)
9. AFNetworking源碼探究(九) —— AFURLSessionManagerTaskDelegate中三個(gè)轉(zhuǎn)發(fā)代理方法詳細(xì)解析(一)
10. AFNetworking源碼探究(十) —— 數(shù)據(jù)解析之?dāng)?shù)據(jù)解析架構(gòu)的分析(一)
11. AFNetworking源碼探究(十一) —— 數(shù)據(jù)解析之子類中協(xié)議方法的實(shí)現(xiàn)(二)
12. AFNetworking源碼探究(十二) —— 數(shù)據(jù)解析之子類中協(xié)議方法的實(shí)現(xiàn)(三)
13. AFNetworking源碼探究(十三) —— AFSecurityPolicy與安全認(rèn)證 (一)
14. AFNetworking源碼探究(十四) —— AFSecurityPolicy與安全認(rèn)證 (二)
15. AFNetworking源碼探究(十五) —— 請求序列化之架構(gòu)分析(一)
16. AFNetworking源碼探究(十六) —— 請求序列化之協(xié)議方法的實(shí)現(xiàn)(二)
17. AFNetworking源碼探究(十七) —— _AFURLSessionTaskSwizzling實(shí)現(xiàn)方法交換(轉(zhuǎn)載)(一)
18. AFNetworking源碼探究(十八) —— UIKit相關(guān)之AFNetworkActivityIndicatorManager(一)
19. AFNetworking源碼探究(十九) —— UIKit相關(guān)之幾個(gè)分類(二)
20. AFNetworking源碼探究(二十) —— UIKit相關(guān)之AFImageDownloader圖像下載(三)
21. AFNetworking源碼探究(二十一) —— UIKit相關(guān)之UIImageView+AFNetworking分類(四)
22. AFNetworking源碼探究(二十二) —— UIKit相關(guān)之UIButton+AFNetworking分類(五)
23. AFNetworking源碼探究(二十三) —— UIKit相關(guān)之UIWebView+AFNetworking分類(六)
24. AFNetworking源碼探究(二十四) —— UIKit相關(guān)之UIProgressView+AFNetworking分類(七)
25. AFNetworking源碼探究(二十五) —— UIKit相關(guān)之UIRefreshControl+AFNetworking分類(八)
回顧
上一篇主要講述了UIRefreshControl+AFNetworking
這個(gè)分類惩坑,將刷新狀態(tài)和任務(wù)狀態(tài)進(jìn)行了綁定和同步。這一篇主要講述AFAutoPurgingImageCache
有關(guān)的緩存也拜。
接口API
按照老慣例以舒,我們還是先看一下接口API文檔。這個(gè)接口文檔包括三個(gè)部分慢哈,兩個(gè)協(xié)議一個(gè)類蔓钟。
- 協(xié)議
AFImageCache
- 協(xié)議
AFImageRequestCache
- 類
AFAutoPurgingImageCache
1. AFImageCache
這個(gè)協(xié)議包括四個(gè)方法
/**
Adds the image to the cache with the given identifier.
@param image The image to cache.
@param identifier The unique identifier for the image in the cache.
*/
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier;
/**
Removes the image from the cache matching the given identifier.
@param identifier The unique identifier for the image in the cache.
@return A BOOL indicating whether or not the image was removed from the cache.
*/
- (BOOL)removeImageWithIdentifier:(NSString *)identifier;
/**
Removes all images from the cache.
@return A BOOL indicating whether or not all images were removed from the cache.
*/
- (BOOL)removeAllImages;
/**
Returns the image in the cache associated with the given identifier.
@param identifier The unique identifier for the image in the cache.
@return An image for the matching identifier, or nil.
*/
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier;
該協(xié)議定義了包括加入、移除卵贱、獲取緩存中的圖片滥沫。
2. AFImageRequestCache
該協(xié)議包含下面幾個(gè)方法,這里注意這個(gè)協(xié)議繼承自協(xié)議AFImageCache
艰赞。
@protocol AFImageRequestCache <AFImageCache>
/**
Asks if the image should be cached using an identifier created from the request and additional identifier.
@param image The image to be cached.
@param request The unique URL request identifing the image asset.
@param identifier The additional identifier to apply to the URL request to identify the image.
@return A BOOL indicating whether or not the image should be added to the cache. YES will cache, NO will prevent caching.
*/
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
/**
Adds the image to the cache using an identifier created from the request and additional identifier.
@param image The image to cache.
@param request The unique URL request identifing the image asset.
@param identifier The additional identifier to apply to the URL request to identify the image.
*/
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
/**
Removes the image from the cache using an identifier created from the request and additional identifier.
@param request The unique URL request identifing the image asset.
@param identifier The additional identifier to apply to the URL request to identify the image.
@return A BOOL indicating whether or not all images were removed from the cache.
*/
- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
/**
Returns the image from the cache associated with an identifier created from the request and additional identifier.
@param request The unique URL request identifing the image asset.
@param identifier The additional identifier to apply to the URL request to identify the image.
@return An image for the matching request and identifier, or nil.
*/
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
@end
根據(jù)請求和標(biāo)識對圖像進(jìn)行是否需要緩存佣谐、加入到緩存或者移除緩存等進(jìn)行操作。
3. AFAutoPurgingImageCache
這個(gè)是這個(gè)類的接口方妖,大家注意下這個(gè)類遵循協(xié)議AFImageRequestCache
狭魂。
/**
The `AutoPurgingImageCache` in an in-memory image cache used to store images up to a given memory capacity. When the memory capacity is reached, the image cache is sorted by last access date, then the oldest image is continuously purged until the preferred memory usage after purge is met. Each time an image is accessed through the cache, the internal access date of the image is updated.
*/
@interface AFAutoPurgingImageCache : NSObject <AFImageRequestCache>
/**
The total memory capacity of the cache in bytes.
*/
// 內(nèi)存緩存總的字節(jié)數(shù)
@property (nonatomic, assign) UInt64 memoryCapacity;
/**
The preferred memory usage after purge in bytes. During a purge, images will be purged until the memory capacity drops below this limit.
*/
// 以字節(jié)為單位清除后的首選內(nèi)存使用情況。 在清除過程中党觅,圖像將被清除雌澄,直到內(nèi)存容量降至此限制以下。
@property (nonatomic, assign) UInt64 preferredMemoryUsageAfterPurge;
/**
The current total memory usage in bytes of all images stored within the cache.
*/
// 當(dāng)前所有圖像內(nèi)存緩存使用的總的字節(jié)數(shù)
@property (nonatomic, assign, readonly) UInt64 memoryUsage;
/**
Initialies the `AutoPurgingImageCache` instance with default values for memory capacity and preferred memory usage after purge limit. `memoryCapcity` defaults to `100 MB`. `preferredMemoryUsageAfterPurge` defaults to `60 MB`.
// 初始化杯瞻,memoryCapcity為100M镐牺,preferredMemoryUsageAfterPurge為60M
@return The new `AutoPurgingImageCache` instance.
*/
- (instancetype)init;
/**
Initialies the `AutoPurgingImageCache` instance with the given memory capacity and preferred memory usage
after purge limit.
@param memoryCapacity The total memory capacity of the cache in bytes.
@param preferredMemoryCapacity The preferred memory usage after purge in bytes.
@return The new `AutoPurgingImageCache` instance.
*/
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity;
@end
內(nèi)存中圖像緩存中的AutoPurgingImageCache
用于存儲圖像到給定內(nèi)存容量。 達(dá)到內(nèi)存容量時(shí)魁莉,圖像緩存按上次訪問日期排序睬涧,然后最舊的圖像不斷清除,直到滿足清除后的首選內(nèi)存使用量旗唁。 每次通過緩存訪問圖像時(shí)畦浓,圖像的內(nèi)部訪問日期都會更新。
AFAutoPurgingImageCache接口及初始化
從接口描述中我們可以看出來检疫,類的初始化規(guī)定了內(nèi)存總的使用量以及清楚以后的內(nèi)存最優(yōu)大小讶请。
- (instancetype)init {
return [self initWithMemoryCapacity:100 * 1024 * 1024 preferredMemoryCapacity:60 * 1024 * 1024];
}
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity {
if (self = [super init]) {
self.memoryCapacity = memoryCapacity;
self.preferredMemoryUsageAfterPurge = preferredMemoryCapacity;
self.cachedImages = [[NSMutableDictionary alloc] init];
NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]];
self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(removeAllImages)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
return self;
}
我們看一下這個(gè)初始化方法中都做了什么事情
- 設(shè)置緩存圖像的字典
self.cachedImages = [[NSMutableDictionary alloc] init];
- 常見和UUID關(guān)聯(lián)的并發(fā)隊(duì)列
NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]];
self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
- 增加移除所有圖像的通知
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(removeAllImages)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
- (BOOL)removeAllImages {
__block BOOL removed = NO;
dispatch_barrier_sync(self.synchronizationQueue, ^{
if (self.cachedImages.count > 0) {
[self.cachedImages removeAllObjects];
self.currentMemoryUsage = 0;
removed = YES;
}
});
return removed;
}
這里就是在上面生成的隊(duì)列中,清空數(shù)組屎媳,重置一些屬性值夺溢。
AFCachedImage接口及初始化
這里我們就看一下AFCachedImage
的接口及初始化论巍。
@interface AFCachedImage : NSObject
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) NSString *identifier;
@property (nonatomic, assign) UInt64 totalBytes;
@property (nonatomic, strong) NSDate *lastAccessDate;
@property (nonatomic, assign) UInt64 currentMemoryUsage;
@end
- (instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier {
if (self = [self init]) {
self.image = image;
self.identifier = identifier;
CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
CGFloat bytesPerPixel = 4.0;
CGFloat bytesPerSize = imageSize.width * imageSize.height;
self.totalBytes = (UInt64)bytesPerPixel * (UInt64)bytesPerSize;
self.lastAccessDate = [NSDate date];
}
return self;
}
這個(gè)初始化方法里面初始化圖像的字節(jié)數(shù),并更新上次獲取數(shù)據(jù)的時(shí)間风响。
協(xié)議方法的實(shí)現(xiàn)
1. AFImageCache協(xié)議的實(shí)現(xiàn)
將圖像根據(jù)標(biāo)識添加到內(nèi)存
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier;
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier {
dispatch_barrier_async(self.synchronizationQueue, ^{
AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier];
AFCachedImage *previousCachedImage = self.cachedImages[identifier];
if (previousCachedImage != nil) {
self.currentMemoryUsage -= previousCachedImage.totalBytes;
}
self.cachedImages[identifier] = cacheImage;
self.currentMemoryUsage += cacheImage.totalBytes;
});
dispatch_barrier_async(self.synchronizationQueue, ^{
if (self.currentMemoryUsage > self.memoryCapacity) {
UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge;
NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate"
ascending:YES];
[sortedImages sortUsingDescriptors:@[sortDescriptor]];
UInt64 bytesPurged = 0;
for (AFCachedImage *cachedImage in sortedImages) {
[self.cachedImages removeObjectForKey:cachedImage.identifier];
bytesPurged += cachedImage.totalBytes;
if (bytesPurged >= bytesToPurge) {
break ;
}
}
self.currentMemoryUsage -= bytesPurged;
}
});
}
這里用到了兩個(gè)阻塞
- 第一個(gè)阻塞
dispatch_barrier_async(self.synchronizationQueue, ^{
AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier];
AFCachedImage *previousCachedImage = self.cachedImages[identifier];
if (previousCachedImage != nil) {
self.currentMemoryUsage -= previousCachedImage.totalBytes;
}
self.cachedImages[identifier] = cacheImage;
self.currentMemoryUsage += cacheImage.totalBytes;
});
這里的作用其實(shí)很清楚嘉汰,就是先根據(jù)image和identify實(shí)例化AFCachedImage
對象。然后在字典中根據(jù)identifier查看是否有AFCachedImage
對象钞诡,如果有的話郑现,那么就減小當(dāng)前使用內(nèi)存的值。并將前面實(shí)例化的AFCachedImage
對象存入字典中荧降,并增加當(dāng)前使用內(nèi)存的值接箫。
- 第二個(gè)阻塞
dispatch_barrier_async(self.synchronizationQueue, ^{
if (self.currentMemoryUsage > self.memoryCapacity) {
UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge;
NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate"
ascending:YES];
[sortedImages sortUsingDescriptors:@[sortDescriptor]];
UInt64 bytesPurged = 0;
for (AFCachedImage *cachedImage in sortedImages) {
[self.cachedImages removeObjectForKey:cachedImage.identifier];
bytesPurged += cachedImage.totalBytes;
if (bytesPurged >= bytesToPurge) {
break ;
}
}
self.currentMemoryUsage -= bytesPurged;
}
});
這里完成的功能是,首先判斷如果當(dāng)前內(nèi)存使用量大于內(nèi)存總量朵诫,那么就需要清理了辛友,這里需要計(jì)算需要清理多少內(nèi)存,就是當(dāng)前內(nèi)存值 - 最優(yōu)內(nèi)存值剪返。然后sortedImages
實(shí)例化字典中所有的圖片废累,并對這些圖片進(jìn)行按照時(shí)間的排序,遍歷這個(gè)排序后的數(shù)組脱盲,逐一從字典中移除邑滨,終止條件就是移除的字節(jié)數(shù)大于上面計(jì)算的要清除的字節(jié)數(shù)值。最后钱反,更新下當(dāng)前內(nèi)存使用的值掖看。
根據(jù)指定標(biāo)識將圖像移出內(nèi)存
- (BOOL)removeImageWithIdentifier:(NSString *)identifier;
- (BOOL)removeImageWithIdentifier:(NSString *)identifier {
__block BOOL removed = NO;
dispatch_barrier_sync(self.synchronizationQueue, ^{
AFCachedImage *cachedImage = self.cachedImages[identifier];
if (cachedImage != nil) {
[self.cachedImages removeObjectForKey:identifier];
self.currentMemoryUsage -= cachedImage.totalBytes;
removed = YES;
}
});
return removed;
}
這個(gè)還是很好理解的,在定義的并行隊(duì)列中面哥,取出identifier
對應(yīng)的AFCachedImage
對象哎壳,然后從字典中移除,并更新當(dāng)前內(nèi)存的值尚卫。
從內(nèi)存中移除所有的圖像
- (BOOL)removeAllImages;
- (BOOL)removeAllImages {
__block BOOL removed = NO;
dispatch_barrier_sync(self.synchronizationQueue, ^{
if (self.cachedImages.count > 0) {
[self.cachedImages removeAllObjects];
self.currentMemoryUsage = 0;
removed = YES;
}
});
return removed;
}
其實(shí)就是一句話归榕,清空字典,更新當(dāng)前內(nèi)存使用值吱涉。
根據(jù)指定的標(biāo)識符從內(nèi)存中獲取圖像
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier;
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier {
__block UIImage *image = nil;
dispatch_sync(self.synchronizationQueue, ^{
AFCachedImage *cachedImage = self.cachedImages[identifier];
image = [cachedImage accessImage];
});
return image;
}
- (UIImage*)accessImage {
self.lastAccessDate = [NSDate date];
return self.image;
}
其實(shí)就是從字典中取值刹泄,并更新上次獲取圖像的時(shí)間。
2. AFImageRequestCache協(xié)議的實(shí)現(xiàn)
根據(jù)請求和標(biāo)識符將圖像加入到內(nèi)存
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
[self addImage:image withIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}
- (NSString *)imageCacheKeyFromURLRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)additionalIdentifier {
NSString *key = request.URL.absoluteString;
if (additionalIdentifier != nil) {
key = [key stringByAppendingString:additionalIdentifier];
}
return key;
}
這里其實(shí)是調(diào)用上面我們講過的那個(gè)根據(jù)identifier取出AFCachedImage
對象的那個(gè)方法怎爵。不過下面這個(gè)identifier是通過調(diào)用下面這個(gè)方法生成的循签。
根據(jù)請求和標(biāo)識符將圖像移出內(nèi)存
- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
return [self removeImageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}
這個(gè),就是還是利用那個(gè)生成indentifier的方法疙咸,獲取identify,然后調(diào)用前面我們講過的方法移除對應(yīng)的圖像风科。
根據(jù)請求和標(biāo)識符獲取內(nèi)存中圖像
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
return [self imageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}
這個(gè)撒轮,就是還是利用那個(gè)生成indentifier的方法乞旦,獲取identify,然后調(diào)用前面我們講過的方法獲取對應(yīng)的圖像题山。
是否將圖像緩存到內(nèi)存
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier {
return YES;
}
這里就是寫死的兰粉,默認(rèn)就是需要進(jìn)行緩存。
后記
本篇主要講述了關(guān)于圖像緩存方面的內(nèi)容顶瞳,包括使用標(biāo)識符或者請求進(jìn)行圖像相關(guān)的緩存操作玖姑。