深入理解SDWebImage-擴(kuò)展

在應(yīng)用SDWebImage過程中,遇到了一些技術(shù)問題和細(xì)節(jié)問題,現(xiàn)在總結(jié)一下,并
進(jìn)行了相關(guān)的技術(shù)擴(kuò)展,SDWebImage確實(shí)是個(gè)值得研究的框架

場景一:當(dāng)我們?cè)谝粋€(gè)頁面中加載特別多的九宮格圖片,那么當(dāng)我們滑動(dòng)頁面肯定會(huì)造成內(nèi)存的暴漲,如何處理那?

首先對(duì)內(nèi)存進(jìn)行監(jiān)聽

//監(jiān)聽內(nèi)存警告
 [[NSNotificationCenter defaultCenter]addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"內(nèi)存暴漲");
        // 1.取消正在下載的操作
        [[SDWebImageManager sharedManager] cancelAll];
        // 2.清除內(nèi)存緩存
        [[SDWebImageManager sharedManager].imageCache clearWithCacheType:SDImageCacheTypeAll completion:nil];
  }];

加載圖片的處理

// SDWebImageLowPriority 當(dāng)UIScrollView滑動(dòng)減速時(shí)開始加載圖片 SDWebImageAvoidDecodeImage由于過多的內(nèi)存消耗,這個(gè)標(biāo)志可以防止解碼圖像。
[self.Pic sd_setImageWithURL:self.url placeholderImage:[UIImage imageNamed:@"logo"] options:SDWebImageLowPriority|SDWebImageAvoidDecodeImage];

場景二:如何讓下載的圖片展示出網(wǎng)頁那種從上到下顯示的效果?

直接設(shè)置SDWebImageProgressiveLoad

[self.image sd_setImageWithURL:self.URL placeholderImage:nil options:SDWebImageProgressiveLoad];
SDWebImageProgressiveLoad

場景三:設(shè)置了SDWebImageRefreshCached,緩存圖片如何更新?

現(xiàn)在提供兩種方法來解決這個(gè)問題 - 舊版本SDWebImage:
首先我們需要探討一下實(shí)現(xiàn)的原理:一種是NSURLCache(NSURL緩存的get請(qǐng)求),一種是SD中自己定義的SDImageCache進(jìn)行緩存。在SDWebImage中我們可以查看SDWebImageRefreshCached是如何定義的 - 如果設(shè)置了該類型,緩存策略依據(jù)NSURLCache而不是SDImageCache,所以可以通過NSURLCache進(jìn)行緩存了;
但是圖片的更新還需要的服務(wù)器的配合才能實(shí)現(xiàn),服務(wù)器如何設(shè)置那?圖片的更新與否取決于你服務(wù)器的cache-control設(shè)置,如果沒有cache-control設(shè)置,那么客戶端就享受不了自動(dòng)更新的功能。首先了解一下cache-control,
終端中輸入命令: curl [url] --head


cache-control

發(fā)現(xiàn)有Cache-Control,說明是可以的。這其實(shí)就是請(qǐng)求照片的過程中,返回來的header信息,這其中還包括一個(gè)名為Last-Modified、數(shù)據(jù)是時(shí)間戳的鍵值對(duì)。
首先為查看HTTP協(xié)議相關(guān)的資料,發(fā)現(xiàn)request header中有一個(gè)名為if-Modified-Since的key,value就是服務(wù)器返回的服務(wù)器最后被修改的時(shí)間;第一次請(qǐng)求過程中由于并沒有攜帶該request header所以if-Modified-Since為空,第一次請(qǐng)求成功之后,將返回的Last-Modified值做為if-Modified-Since的值傳回給服務(wù)器。這樣后臺(tái)就會(huì)對(duì)if-Modified-Since和Last-Modified進(jìn)行比較,如果客戶端圖片已經(jīng)過期,那么返回狀態(tài)碼200、Last-modified和圖片內(nèi)容,客戶端重新將Last-modified存儲(chǔ)到if-Modified-Since;如果客戶端返回的是304 not Modified、則不會(huì)返回last-Modified、圖片內(nèi)容,說明圖片沒有更新,直接拿緩存中數(shù)據(jù)就行。
回到SDWebImage上,通過查看老的SDWebImageDownloader版本代碼發(fā)現(xiàn),它開放了一個(gè)headersFilter的block,我們可以在這個(gè)block中追加額外的header,所以我們可以在例如AppDelegate didFinishLaunching的地方追加如下代碼:

SDWebImageDownloader *imgDownloader = SDWebImageManager.sharedManager.imageDownloader;
imgDownloader.headersFilter  = ^NSDictionary *(NSURL *url, NSDictionary *headers) {
NSFileManager *fm = [[NSFileManager alloc] init];
NSString *imgKey = [SDWebImageManager.sharedManager cacheKeyForURL:url];
NSString *imgPath = [SDWebImageManager.sharedManager.imageCache defaultCachePathForKey:imgKey];
NSDictionary *fileAttr = [fm attributesOfItemAtPath:imgPath error:nil];

NSMutableDictionary *mutableHeaders = [headers mutableCopy];

NSDate *lastModifiedDate = nil;

if (fileAttr.count > 0) {
    if (fileAttr.count > 0) {
        lastModifiedDate = (NSDate *)fileAttr[NSFileModificationDate];
    }

}
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
formatter.dateFormat = @"EEE, dd MMM yyyy HH:mm:ss z";

NSString *lastModifiedStr = [formatter stringFromDate:lastModifiedDate];
lastModifiedStr = lastModifiedStr.length > 0 ? lastModifiedStr : @"";
[mutableHeaders setValue:lastModifiedStr forKey:@"If-Modified-Since"];

return mutableHeaders;
復(fù)制代碼
};

SDWebImage
然后加載圖片的地方之前怎么寫就怎么寫,但是option中一定要加上SDWebImageRefreshCached
另外一種方法:
在SDWebImageManager.m大約167行的地方加上
// remove SDWebImageDownloaderUseNSURLCache flag downloaderOptions &= ~SDWebImageDownloaderUseNSURLCache;
變成了

if (cachedImage && options & SDWebImageRefreshCached) {

            // force progressive off if image already cached but forced refreshing
            downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
            // remove SDWebImageDownloaderUseNSURLCache flag
            downloaderOptions &= ~SDWebImageDownloaderUseNSURLCache;
            // ignore image read from NSURLCache if image if cached but force refreshing
            downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
        }

參考鏈接

注意:最新版本中已經(jīng)解決了這個(gè)問題
在之前的版本中,如果服務(wù)端更新了圖片,雖然設(shè)置了SDWebImageRefreshCached,還是拿到的老圖片,在最新版5.1.0中已經(jīng)解決了這個(gè)問題 - 應(yīng)用的SDImageCache,通過每次圖片的重新網(wǎng)絡(luò)請(qǐng)求,和當(dāng)前的緩存數(shù)據(jù)做比較,如果不同那么就將新請(qǐng)求到的image通過block返回。

//判斷是否更新了,包括SDWebImageDownloaderIgnoreCachedResponse/本地緩存對(duì)比
if (self.options & SDWebImageDownloaderIgnoreCachedResponse && [self.cachedData isEqualToData:imageData]) {
                   //如果和本地緩存相同,那么返回SDWebImageErrorCacheNotModified
                    self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCacheNotModified userInfo:nil];
                    // call completion block with not modified error
                    [self callCompletionBlocksWithError:self.responseError];
                    [self done];
//如果沒有更新,那么在子線程進(jìn)圖片處理
 } else {
                    // decode the image in coder queue
                    dispatch_async(self.coderQueue, ^{
                        @autoreleasepool {

另外也可以應(yīng)用Cache-control,但是最新版的并沒有暴露headersFilter,而是暴露了

/**
 * Set a value for a HTTP header to be appended to each download HTTP request.
 *
 * @param value The value for the header field. Use `nil` value to remove the header field.
 * @param field The name of the header field to set.
 */
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field;

[[SDWebImageDownloader sharedDownloader] setValue:@"" forHTTPHeaderField:@""];
通過這個(gè)方法,我們可以將if-Modified-Since 傳入SDWebImageDownloader即可,同樣還是需要在option中傳入SDWebImageRefreshCached

場景四:圖片的解壓縮問題?

設(shè)置SDWebImageAvoidDecodeImage,這個(gè)option到底是如何實(shí)現(xiàn)在子線程解壓縮圖片的那?

圖片的加載工作流

將一張圖片從磁盤加載到內(nèi)存然后渲染到屏幕上,這個(gè)過程的消耗其實(shí)非常的大,會(huì)明顯降低界面的幀速率,當(dāng)滾動(dòng)的時(shí)候會(huì)加劇這一情況,因?yàn)閮?nèi)容變化的太快,需要更快的處理速度才能保持在60FPS的幀速率。
首先考慮一下加載的工作流程:

  • [UIImage imageWithContentsOfFile:]使用Image I/O創(chuàng)建CGImageRef內(nèi)存映射數(shù)據(jù)。此時(shí),圖像尚未解碼。
  • 返回的數(shù)據(jù)被返回給UIImageView。
  • 隱式CATransaction捕獲這些層樹修改。
  • 在主運(yùn)行循環(huán)的下一次迭代中,Core Animation提交隱式事物,這可能涉及創(chuàng)建已設(shè)置為層內(nèi)容的任何圖像的副本。根據(jù)圖像,復(fù)制它涉及一下部分或全部步驟:
    • 緩沖區(qū)被分配用于管理文件和解壓縮操作
    • 文件數(shù)據(jù)從磁盤讀入內(nèi)存
    • 壓縮的圖像數(shù)據(jù)被解壓縮成其未壓縮的位圖形式,這通常是CPU密集型操作
    • 然后Core Animation使用未壓縮的位圖數(shù)據(jù)來渲染涂層

擴(kuò)展:Core Animation不僅能用來做動(dòng)畫,實(shí)際上是一個(gè)叫做Layer kit這么一個(gè)不怎么和動(dòng)畫相關(guān)的名字演變來的。Core Animation其實(shí)是一個(gè)復(fù)合引擎,它的指責(zé)是盡快的組合屏幕上不同的可視內(nèi)容,這個(gè)內(nèi)容是被分解成獨(dú)立的涂層,存儲(chǔ)在一個(gè)叫圖層樹的體系之中,這個(gè)樹形成了UIKit以及在iOS程序中你能在屏幕上看到的一切的基礎(chǔ)。
時(shí)鐘信號(hào):垂直同步信號(hào)V-Sync/水平同步信號(hào)H-Sync,有這兩個(gè)信號(hào)來按照信號(hào)時(shí)間,定時(shí)進(jìn)行界面的相應(yīng)展示
CPU:計(jì)算視圖frame,圖片的解壓縮
GPU:紋理繪制,頂點(diǎn)變換,像素點(diǎn)的填充,渲染
當(dāng)圖片過大那么CPU解壓就會(huì)非常耗時(shí),那么在當(dāng)前的水平同步信號(hào)到來到結(jié)束這一段時(shí)間內(nèi),如果沒有解壓或者渲染完成,那么到下一個(gè)H-Sync信號(hào)到來時(shí)就會(huì)出現(xiàn)拖尾現(xiàn)象 - 卡頓。

位圖

如果不進(jìn)行解壓縮,直接渲染是不行的,必須要解壓成位圖,那么什么是位圖那?

UIImage *image = [UIImage imageNamed:@"logo.png"];
CFDataRef mapData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));

通過CGDataProviderCopyData獲取到的mapData就是位圖,可以嘗試打印,
發(fā)現(xiàn)位圖其實(shí)就是一個(gè)像素?cái)?shù)組,有一個(gè)獲取圖片解壓后位圖大小的公式
圖片像素寬 圖片像素高4 = 位圖大小
事實(shí)上,不管是 JPEG 還是 PNG 圖片,都是一種壓縮的位圖圖形格式。只不過 PNG 圖片是無損壓縮,并且支持 alpha 通道,而 JPEG 圖片則是有損壓縮,可以指定 0-100% 的壓縮比。值得一提的是,在蘋果的 SDK 中專門提供了兩個(gè)函數(shù)用來生成 PNG 和 JPEG 圖片:

// return image as PNG. May return nil if image has no CGImageRef or invalid bitmap format
UIKIT_EXTERN NSData * __nullable UIImagePNGRepresentation(UIImage * __nonnull image);

// return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least)                           
UIKIT_EXTERN NSData * __nullable UIImageJPEGRepresentation(UIImage * __nonnull image, CGFloat compressionQuality);

所以其實(shí)我們平時(shí)的PNG/JPEG,都是壓縮之后的圖片,需要對(duì)圖片進(jìn)行解壓獲取圖片的位圖進(jìn)行渲染。

解壓縮API

默認(rèn)情況下SDWebImage獲取到圖片的壓縮文件之后,需要用戶在UIImageView賦值的同時(shí)進(jìn)行解壓縮,但是在SDWebImage中如果設(shè)置了SDWebImageAvoidDecodeImage,根本原理是在子線程解壓成位圖,并進(jìn)行繪制。用到的主要API就是CGBitmapContextCreate:,

CG_EXTERN CGContextRef __nullable CGBitmapContextCreate(void * __nullable data,
    size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow,
    CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo)
    CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
   

這個(gè)函數(shù)就是繪制一個(gè)位圖上下文。

  • data :如果不為 NULL ,那么它應(yīng)該指向一塊大小至少為 bytesPerRow * height 字節(jié)的內(nèi)存;如果 為 NULL ,那么系統(tǒng)就會(huì)為我們自動(dòng)分配和釋放所需的內(nèi)存,所以一般指定 NULL 即可;

  • width 和height :位圖的寬度和高度,分別賦值為圖片的像素寬度和像素高度即可;

  • bitsPerComponent :像素的每個(gè)顏色分量使用的 bit 數(shù),在 RGB 顏色空間下指定 8 即可;

  • bytesPerRow :位圖的每一行使用的字節(jié)數(shù),大小至少為 width * bytes per pixel 字節(jié)。當(dāng)我們指定 0/NULL 時(shí),系統(tǒng)不僅會(huì)為我們自動(dòng)計(jì)算,而且還會(huì)進(jìn)行 cache line alignment 的優(yōu)化

  • space :就是我們前面提到的顏色空間,一般使用 RGB 即可;

  • bitmapInfo :位圖的布局信息,alpha/顏色分量是否是浮點(diǎn)數(shù)/像素格式的字節(jié)順序。如果有alpha那么用kCGImageAlphaPremultipliedFirst,否則用kCGImageAlphaNoneSkipFirst。像素格式(大端小端/16或者32未)使用kCGBitmapByteOrder32Host(關(guān)于布局信息的更多信息)

查看SDWebImage中的解壓

首先獲得圖片是否有alpha

//判斷是否有alpha
+ (BOOL)CGImageContainsAlpha:(CGImageRef)cgImage {
    if (!cgImage) {
        return NO;
    }
    //獲取圖片的alpha信息
    CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage);
    //kCGImageAlphaNone沒有alpha
    //kCGImageAlphaNoneSkipFirst在RGB透明通道下,alpha沒有在最高有效位
    //kCGImageAlphaNoneSkipLast在RGB透明通道下,alpha沒有在最低有效位
    //這三者都得包括
    BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
                      alphaInfo == kCGImageAlphaNoneSkipFirst ||
                      alphaInfo == kCGImageAlphaNoneSkipLast);
    return hasAlpha;
}

然后根據(jù)Bitmap構(gòu)造上下文函數(shù)生成bitmap上下文,并對(duì)圖片進(jìn)行transform,獲取圖片上下文

+ (CGImageRef)CGImageCreateDecoded:(CGImageRef)cgImage orientation:(CGImagePropertyOrientation)orientation {
    if (!cgImage) {
        return NULL;
    }
    //獲取圖片的像素寬高
    size_t width = CGImageGetWidth(cgImage);
    size_t height = CGImageGetHeight(cgImage);
    if (width == 0 || height == 0) return NULL;
    size_t newWidth;
    size_t newHeight;
    //查看當(dāng)前圖片的展示方式是否正確,對(duì)width/height進(jìn)行調(diào)整
    switch (orientation) {
        case kCGImagePropertyOrientationLeft:
        case kCGImagePropertyOrientationLeftMirrored:
        case kCGImagePropertyOrientationRight:
        case kCGImagePropertyOrientationRightMirrored: {
            //kCGImagePropertyOrientationRightMirrored這種情況應(yīng)該交換寬高
            newWidth = height;
            newHeight = width;
        }
            break;
        default: {
            //否則不需要處理
            newWidth = width;
            newHeight = height;
        }
            break;
    }
    //是否有alpha通道
    BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
    
    //像素格式中的字節(jié)順序是系統(tǒng)提供的32位主機(jī)字節(jié)順序
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
    //將像素格式中用位域技術(shù)添加alpha信息
    bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
    //獲得位圖的上下文
    //默認(rèn)的顏色空間是CGColorSpaceCreateDeviceRGB()
    //bytesPerRow 每一行的位圖大小設(shè)置為0,系統(tǒng)進(jìn)行自動(dòng)計(jì)算并且進(jìn)行優(yōu)化
    //每一個(gè)像素的顏色分量bit數(shù)是8
    CGContextRef context = CGBitmapContextCreate(NULL, newWidth, newHeight, 8, 0, [self colorSpaceGetDeviceRGB], bitmapInfo);
    if (!context) {
        return NULL;
    }
    
    //圖片進(jìn)行反轉(zhuǎn),保證展示出來的是沒有transform的圖片
    CGAffineTransform transform = SDCGContextTransformFromOrientation(orientation, CGSizeMake(newWidth, newHeight));
    CGContextConcatCTM(context, transform);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); // The rect is bounding box of CGImage, don't swap width & height
    //獲得當(dāng)前上下文中的位圖對(duì)應(yīng)的圖片
    CGImageRef newImageRef = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    
    return newImageRef;
}

這就是整個(gè)的圖片在自線程利用CGBitmapContextCreate進(jìn)行解壓縮的過程

雷純鋒的blog
圖片的解壓縮和渲染過程

場景五:SD中核心方法中context使用問題?

/**
 * 通過URL加載圖片,如果cache中存在就從cache中獲取,否則開始下載
 *
 * @param url            傳入的image的url
 * @param options        獲取圖片的方式
 * @param context        獲取
 * @param progressBlock  獲得圖片的進(jìn)度(注意是在子隊(duì)列中)
 * @param completedBlock  完成獲取之后的回掉block
 * @return  返回一個(gè)SDWebImageCombinedOperation對(duì)象,用于表示當(dāng)前的圖片獲取任務(wù),在這個(gè)對(duì)象中可以取消獲取圖片任務(wù)
 */
- (nullable SDWebImageCombinedOperation *)loadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageOptions)options
                                                   context:(nullable SDWebImageContext *)context
                                                  progress:(nullable SDImageLoaderProgressBlock)progressBlock
                                                 completed:(nonnull SDInternalCompletionBlock)completedBlock;

其中context中對(duì)應(yīng)了很多的業(yè)務(wù)場景,我們可以自己定義

(1). SDWebImageContextImageTransformer - 處理加載出來的圖片,比如翻轉(zhuǎn)圓角等

(2). SDWebImageContextCacheKeyFilter - 指定圖片的緩存key

(3). SDWebImageContextCacheSerializer - 轉(zhuǎn)換需要緩存的圖片格式

(1)、SDWebImageContextImageTransformer 其對(duì)應(yīng)的是遵守SDImageTransformer協(xié)議的類,查看系統(tǒng)方法可以找到具體的圖片處理類型:


@protocol SDImageTransformer <NSObject>

@required
/**
 @return 在原始緩存中最后添加的自定義cache key
 */
@property (nonatomic, copy, readonly, nonnull) NSString *transformerKey;

/**
 調(diào)用當(dāng)前方法實(shí)現(xiàn)圖片的處理
 @param image  處理之后的圖片
 @param key 原始圖片關(guān)聯(lián)的cache key 
 @return 處理之后的圖片
 */
- (nullable UIImage *)transformedImageWithImage:(nonnull UIImage *)image forKey:(nonnull NSString *)key;

@end

#pragma mark - Pipeline

/**
 //可以傳入一個(gè)NSArray<SDImageTransformer>數(shù)組,按順序做轉(zhuǎn)換
 */
@interface SDImagePipelineTransformer : NSObject <SDImageTransformer>

/**
 */
@property (nonatomic, copy, readonly, nonnull) NSArray<id<SDImageTransformer>> *transformers;

- (nonnull instancetype)init NS_UNAVAILABLE;
+ (nonnull instancetype)transformerWithTransformers:(nonnull NSArray<id<SDImageTransformer>> *)transformers;

@end

/**
 添加圓角
 */
@interface SDImageRoundCornerTransformer: NSObject <SDImageTransformer>

@property (nonatomic, assign, readonly) CGFloat cornerRadius;

@property (nonatomic, assign, readonly) SDRectCorner corners;

@property (nonatomic, assign, readonly) CGFloat borderWidth;

@property (nonatomic, strong, readonly, nullable) UIColor *borderColor;

- (nonnull instancetype)init NS_UNAVAILABLE;
+ (nonnull instancetype)transformerWithRadius:(CGFloat)cornerRadius corners:(SDRectCorner)corners borderWidth:(CGFloat)borderWidth borderColor:(nullable UIColor *)borderColor;

@end

/**
 調(diào)整大小
 */
@interface SDImageResizingTransformer : NSObject <SDImageTransformer>

@property (nonatomic, assign, readonly) CGSize size;

@property (nonatomic, assign, readonly) SDImageScaleMode scaleMode;

- (nonnull instancetype)init NS_UNAVAILABLE;
+ (nonnull instancetype)transformerWithSize:(CGSize)size scaleMode:(SDImageScaleMode)scaleMode;

@end

/**
 裁剪
 */
@interface SDImageCroppingTransformer : NSObject <SDImageTransformer>

@property (nonatomic, assign, readonly) CGRect rect;

- (nonnull instancetype)init NS_UNAVAILABLE;
+ (nonnull instancetype)transformerWithRect:(CGRect)rect;

@end

/**
 翻轉(zhuǎn)
 */
@interface SDImageFlippingTransformer : NSObject <SDImageTransformer>

@property (nonatomic, assign, readonly) BOOL horizontal;

@property (nonatomic, assign, readonly) BOOL vertical;

- (nonnull instancetype)init NS_UNAVAILABLE;
+ (nonnull instancetype)transformerWithHorizontal:(BOOL)horizontal vertical:(BOOL)vertical;

@end

/**
 旋轉(zhuǎn)
 */
@interface SDImageRotationTransformer : NSObject <SDImageTransformer>

@property (nonatomic, assign, readonly) CGFloat angle;

@property (nonatomic, assign, readonly) BOOL fitSize;

- (nonnull instancetype)init NS_UNAVAILABLE;
+ (nonnull instancetype)transformerWithAngle:(CGFloat)angle fitSize:(BOOL)fitSize;

@end

#pragma mark - Image Blending

/**
 添加色彩
 */
@interface SDImageTintTransformer : NSObject <SDImageTransformer>

@property (nonatomic, strong, readonly, nonnull) UIColor *tintColor;

- (nonnull instancetype)init NS_UNAVAILABLE;
+ (nonnull instancetype)transformerWithColor:(nonnull UIColor *)tintColor;

@end

#pragma mark - Image Effect

/**
 添加模糊
 */
@interface SDImageBlurTransformer : NSObject <SDImageTransformer>

@property (nonatomic, assign, readonly) CGFloat blurRadius;

- (nonnull instancetype)init NS_UNAVAILABLE;
+ (nonnull instancetype)transformerWithRadius:(CGFloat)blurRadius;

@end

#if SD_UIKIT || SD_MAC
/**
 添加濾鏡
 */
@interface SDImageFilterTransformer: NSObject <SDImageTransformer>

@property (nonatomic, strong, readonly, nonnull) CIFilter *filter;

- (nonnull instancetype)init NS_UNAVAILABLE;
+ (nonnull instancetype)transformerWithFilter:(nonnull CIFilter *)filter;

@end

通過這幾個(gè)類可以對(duì)SDWebImageContextImageTransformer中value進(jìn)行自定義,以達(dá)到我們的需要

(2)、SDWebImageContextCacheKeyFilter - 圖片的指定緩存key,達(dá)到自定義緩存的目的,生成SDWebImageCacheKeyFilter對(duì)象。
一種是直接賦值給SDWebImageManager,另一種是放到context中處理。
在UIImageView調(diào)用加載圖片時(shí),設(shè)置下面代碼,自定義緩存key。
注意block回調(diào)是在global queue中進(jìn)行的。

設(shè)置SDWebImageCacheKeyFilter,在SD內(nèi)部根據(jù)URL緩存數(shù)據(jù)時(shí),會(huì)進(jìn)入block中,可以對(duì)url進(jìn)行自定義
SDWebImageManager.sharedManager.cacheKeyFilter =[SDWebImageCacheKeyFilter cacheKeyFilterWithBlock:^NSString * _Nullable(NSURL * _Nonnull url) {
    url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
    return [url absoluteString];
 }];

(3)、SDWebImageContextCacheSerializer - 緩存的圖片格式 SDWebImageCacheSerializer對(duì)象
一種是直接賦值給SDWebImageManager,另一種是放到context中處理。
當(dāng)webP格式的圖片data數(shù)據(jù)從磁盤讀取時(shí),會(huì)比普通格式的圖片讀取更加費(fèi)時(shí),所以我們下載完webP格式的data數(shù)據(jù),將其圖片格式變?yōu)镻NG/JPEG,然后將NSData數(shù)據(jù)放入磁盤,這樣下次讀取的時(shí)候速度會(huì)更快。
在UIImageView調(diào)用加載圖片時(shí),設(shè)置下面代碼,自定義緩存圖片格式。
注意block回調(diào)是在global queue中進(jìn)行的。

 SDWebImageManager.sharedManager.cacheSerializer = [SDWebImageCacheSerializer cacheSerializerWithBlock:^NSData * _Nullable(UIImage * _Nonnull image, NSData * _Nullable data, NSURL * _Nullable imageURL) {
    SDImageFormat format = [NSData sd_imageFormatForImageData:data];
    switch (format) {
        case SDImageFormatWebP:
            return image.images ? data : nil;
        default:
            return data;
    }
}];

歡迎關(guān)注我的公眾號(hào),專注iOS開發(fā)、大前端開發(fā)、跨平臺(tái)技術(shù)分享。


iOS開發(fā)之家
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 圖片在計(jì)算機(jī)中如何存儲(chǔ)和表示? 常見的圖片格式 JPEG 是目前最常見的圖片格式,它誕生于1992年,是一個(gè)很古老...
    流河蝦米閱讀 7,888評(píng)論 1 17
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,626評(píng)論 1 32
  • 談?wù)?iOS 中圖片的解壓縮 FEB 20TH, 2017 10:47 AM 對(duì)于大多數(shù) iOS 應(yīng)用來說,圖片往...
    Dev_hell03W閱讀 535評(píng)論 0 0
  • 下載 下載管理器 SDWebImageDownLoader作為一個(gè)單例來管理圖片的下載操作。圖片的下載是放在一個(gè)N...
    wind_dy閱讀 1,660評(píng)論 0 1
  • 不積跬步,無以至千里。 有的時(shí)候,走上街頭去拍照真的很難。尤其是面對(duì)陌生人質(zhì)疑的眼光,使得你不敢將鏡頭對(duì)準(zhǔn)他們,而...
    LikeAKid閱讀 558評(píng)論 0 0

友情鏈接更多精彩內(nèi)容