SDWebImage

目录


1.❤❤

<1> SDWebImageCompat

.h -- 兼容(GC+Version+UI+NS_ENUM_OPTIONS+属性语义&内存管理+保持主线程执行block的宏)

.m -- inline SDScaledImageForKey -> i. 所有图片设置对应scale ii. 动画图片返回

<2> SDWebImageOperation
@protocol SDWebImageOperation -- -cancel



2.❤

<1> SDWebImageDownloader -- 是对operationQueue的一层封装

.h
-- SDWebImageDownloaderOptions(8) + SDWebImageDownloaderExecutionOrder(2)
-- Start/StopNotification + Progress/CompletedBlock + HeadersFilterBlock
-- +sharedDownloader , -downloadImageWithURL:~~

.m
+initialize(SDNetworkActivityIndicator),+sharedDownloader,-init(init+webp)
-- HTTPHeadersgetter&setter + 最大和当前并发数返回queue,暂停队列等
-- downloadImageWithURL:
(request->operation(init:progress:复制URLCallbacks[url] + 异步主队列遍历-调用其中progressBlock, completed:barrier_sync(复制+完成移除) + 遍历-调用其中completedBlock,cancelled:barrier_async(移除))+证书+队列优先级+operationqueue) <- addProgressCallback:(dispatch_barrier_sync(封装block)做阻塞)

<2> SDWebImageDownloaderOperation -- 是对Operation的一层封装(继承Operation实践)

标配:

-start:(检查取消,后台运行(expirationBlock:停止runloop,stop通知,executing,finished,触发start通知,开启runloop用于调用NSURLConnection的Delegate)

手动触发 _finished & _executing的KVO

-isConcurrent

.h
-- Start/Stop/Finish/ReceiveResponse通知 + -initWithRequest:~~

.m
-- init

-- NSURLConnectionDelegate:

~~: didReceiveData:

connectionDidFinishLoading:

didFailWithError:

willCacheResponse: -- 这里返回nil,responses将不会缓存,详参 NSURLCache

willSendRequestForAuthChallenge:


<1> SDImageCache

.h
SDImageCacheType + Query/CheckCacheCompletedBlock/CalculateSizeBlock

+sharedImageCache

initWithNamespace:diskCacheDirectory:

makeDiskCachePath:(NSCachesDirectory缓存目录)

addReadOnlyCachePath:(懒加载+不包含添加)
storeImage:recalculateFromImage:imageData:forKey:toDisk:
queryDiskCacheForKey:done:imageFromMemoryCacheForKey:

imageFromDiskCacheForKey:(给key,查找内存和磁盘(ifNeed,内存缓存(NSCache)),返回)

removeImageForKey:fromDisk:withCompletion:

clearMemory,
clearDiskOnCompletion:,
cleanDiskWithCompletionBlock:

calculateSizeWithCompletionBlock(拿size和cnt丢到block中)

diskImageExistsWithKey:(fileExistsAtPath)/completion:
异步串行队列(fileExistsAtPath + 异步主队列completionBlock)

cachePathForKey:inPath:(缓存路径(diskCachePath/自定义Path + MD5filename))

.m

AutoPurgeCache

init: UIApplicationDidReceiveMemoryWarningNotification - removeAllObjects,dealloc: remove

ImageDataHasPNGPreffix,SDCacheCostForImage (像素数)

initWithNamespace:diskCacheDirectory:(各种初始化+3通知)

storeImage:recalculateFromImage:imageData:forKey:toDisk: ( 内存缓存,磁盘缓存(异步串行队列(i.判断PNG_JPEG->data ii.缓存目录+缓存路径->缓存文件+禁止iCloud备份)) )

diskImageDataBySearchingAllPathsForKey: 缓存路径Data(diskCachePath) +自定义路径Data(customPaths)

diskImageForKey: (所有路径找data->image->scale调整->提前解码)

queryDiskCacheForKey:done: (给key-查找内存(doneBlock)和磁盘(ifNeed,内存缓存(NSCache)+doneBlock)-返回operation)

removeImageForKey:fromDisk:withCompletion: (remove内存缓存+ remove磁盘缓存(removeItemAtPath: +异步主队列completion()) )

clearDiskOnCompletion: (异步串行(remove->create缓存目录+异步主队列completion()))

cleanDiskWithCompletionBlock:(目录枚举器(diskCacheDictionaryURL + resourceKeys) -> 移除过期URLs,超出最大内存清理为一半)

backgroundCleanDisk(后台任务过期Block +清理磁盘过期缓存)

4.❤❤❤


<1> SDWebImageManager

.h

SDWebImageOptions(12) + Completion/WithFinishedBlock,CacheKeyFilterBlock

delegate: -shouldDownloadImageForURL:,- transformDownloadedImage:withURL:

+sharedManager,downloadImageWithURL:~~,缓存+是否缓存

.m

+sharedManager

+init缓存相关(5+1)

downloadImageWithURL:~~ (SDCache的doneBlock( downloader的封装 ) )

SDWebImageCombinedOperation:-setCancelBlock:,-cancel

<2> SDWebImageDecoder

.h -- decodedImageWithImage:

.m -- 不解码动图,alpha通道信息(alpha通道,不做解码),颜色空间模型(其中之一),上下文中画图 -> 返回带alpha的图

<3> SDWebImagePrefetcher

.h
SDWebImagePrefetcherDelegate:didPrefetchURL:finishedCnt:totalCnt ,didFinishWithTotalCnt:skippedCnt:

-- PrefetcherProgress/CompletionBlock, sharedImagePrefetcher:, initWithImgManager:,prefetchURLs:,prefetchURLs:~~,cancel~

.m
+sharedImagePrefetcher

最大并发数(downloader的)

startPrefetchingAtIndex:~~ (self.manager downloadImageWithURL:)

reportStatus(调用delegate)

prefetchURLs:~~(startPrefetchingAtIndex)

5.❤❤❤❤❤❤

<1> MKAnnotationView+WebCache

.h
-- sd_setImageWithURL: /placeholderImage: /options: / completedBlock,sd_cancelCurrentImageLoad,sd_imageURL

.m -- sd_imageURL(关联对象&imageURLKey)

对外简易接口调用内部最复杂方法
->sd_setImageWithURL:placeholderImage:options: completedBlock(取消当前图片加载,setURL关联对象,url有->SDWebImageManager:completed: (主线程更新UI,competedBlock)->关联operation,没有=>completedBlock(error))sd_cancelCurrentImageLoad(WebCacheOperation:sd_cancelCur)

<2> UIButton+WebCache -- keyString+@(state)

.h
sd_currentImageURL
sd_imageURLForState:
sd_setImageWithURL:forState:/placeholderImage:/options:/completedBlock
setBgImgWithURL:forState:/placeholderImage:/options:/completedBlock,cancelImageLoadForState:
cancelBgImageLoadForState:

.m
懒加载-imageURLStorage(&imageURLStorageKey关联字典) - Dict[@(state)]=url(维护不同state的url)

sd_setImageWithURL:forState:/placeholderImage:/options:/completedBlock(img:state:,取消(key+state),没有url->移除state+主线程completedBlock,有->set,SDWebImageManager(image)+关联operation)

setBgImgWithURL:forState:/placeholderImage:/options:/completedBlock
(取消(key+state),bgImg:state:,有url->SDWebImageManager(BgImage)+关联operation,没有url->主线程completedBlock)

sd_set/cancel-Image/BgImage: keyString+@(state)

<3> UIImage+MultiFormat

.h -- sd_imageWithData:

.m -- sd_imageWithData:(imageContentType: i. 动图,ii. WebP iii. 其他+调整方向)

i. NSData+ImageContentType -- sd_contentTypeForImageData:

ii. UIImage+GIF
-- sd_animatedGIFNamed, ~~GIFWithData, ImageByScalingAndCroppingToSize:

iii. UIImage+WebP -- sd_imageWithWebPData:

<4> UIImageView+HighlightedWebCache

.h

sd_setHighlightedImageWithURL:/options:/progressBlock:/completedBlock,cancelCurrentHighlightedImageLoad

.m -- (取消,有url->SDWebImageManager(**highlightedImage**)+关联operation,没有url->主线程completedBlock))

<5> UIImageView+WebCache

.h
-- sd_imageURL
sd_setImageWithURL:/placeholderImage:/options:/progressBlock/completedBlock,sd_setImageWithPreviousCacheImageWithURL:~~
set动画图片WithURLs,取消图片/动画图片,显示活动指示器/样式

.m

i. sd_setImageWithURL:/placeholderImage:/options:/progressBlock/completedBlock:(取消当前,url关联对象,延迟选项,url有->动画+SDWebImageManager+关联operation,没有->completedBlock)

ii. sd_setImageWithPreviousCacheImageWithURL:~~(url->key,给key-查找内存和磁盘(ifNeed,内存缓存(NSCache))-返回)

iii. sd_imageURL:(关联对象get)

iv. sd_setAnimationImagesWithURLs:
(取消,遍历arrayOfURLs(SDWebImageManager:completed:(叠加image->animationImages)),添加operationsArray关联对象)

&TAG_ACTIVITY_INDICATOR(UIActivityIndicatorView *)
&TAG_ACTIVITY_SHOW(BOOL)
&TAG_ACTIVITY_STYLE(UIActivityIndicatorViewStyle)
&imageURLKey
addActivityIndicator(初始化布局+动画)

<6> UIImage+WebCacheOperation -- 关联字典

.h

sd_setImageLoadOperation:forKey:
sd_cancelImageLoadOperationWithKey:

sd_removeImageLoadOperationWithKey:

.m

operationDict(字典关联对象,有返回,没有set)
sd_setImageLoadOperation:forKey:(取消图片加载,set入关联字典)
sd_cancelImageLoadOperationWithKey:(关联字典中去Key对象,是NSArray->for遍历cancel,不是但遵循协议->调用协议cancel,remove)
sd_removeImageLoadOperationWithKey:(remove出关联字典)



UIImageView+WebCache调用过程:

sd_setImageWithURL:placeholderImage:options:progress:completed:
根据需要设置placeholder,通过url调用SDWebImageManager的downloadImageWithURL:options:progress:completed: -- 再通过url转换为key,调用SDImageCachequeryDiskCacheForKey:done: -- 分别在内存缓存和**磁盘缓存**(如果需要,拿出时缓存到内存中)中查找,找到回调doneBlock(image, cacheType)。

1.没有图片+默认允许下载,调用SDWebImageDownloaderdownloadImageWithURL:options:progress:completed: -- 再调用addProgressCallback:completedBlock:forURL:createCallback: -- progressBlockcompletedBlock 封装进URLCallbacks
首次创建会回调createCallback()
初始化SDWebImageDownloaderOperation
initWithRequest:options:progress:completed:cancelled: -- 设置block。然后把返回的operation加入downloadQueue。
当执行Queue时候,会取出operation, 执行其start方法 [self.connection start]
在下载过程中<1> connection:didReceiveData:方法会回调progressBlock(,) + <2> connectionDidFinishLoading:会回调completedBlock(~~ ) + <3> cancelInternal会回调cancelBlock()
——> <1> 在回调progressBlock(,)中,拿出之前存入的progressBlock(,)再回调 <2> completedBlock(~~ )回调中,同样再回调。回调后,i. 可以用delegate变换下载的图片,变换后缓存。再回调completedBlock(~) ii. 也可以不变换直接缓存,再回调completedBlock(~) <3> cancelBlock() -> [URLCallbacks remove:url]
->上面的 <1> 回调回来的progressBlock会调用用户编写的代码,用于更新进度条之类的工作。上面的** i. ii. **回调的completedBlock会更新UIImageViewimage。

2.有图片,回调 completedBlock(image, nil, cacheType, YES, url) —>同步更新UIImageViewimage

学习点:





疑问:

1. 忽略 performSelector 泄露的警告

pragma clang diagnostic push
pragma clang diagnostic ignored"-Warc-performSelector-leaks"
idactivityIndicator =[NSClassFromString(@"SDNetworkActivityIndicator")performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
pragma clang diagnostic pop

答案:

谈谈Objective-C的警告

(1) 填写为-Wall -Wno-unused-variable即可打开“全部”警告
(2) -Wall + -Wextra + -Weverything

(3)
<1> Clang提供了我们自己加入警告或者暂时关闭警告的办法。

警告类型:-W#pragma-messages, -W#warnings

<2> 比如在发布一些需要API Key之类的类库时,可以使用这个方法来提示别的开发者别忘了输入必要的信息。



<3> 全局关闭警告 -- Other C Flags - -Wno-... ,如 -Wextra -Wno-sign-compare

某几个文件开启或禁用警告 -- Compile Source - 编译标识

某几行关闭某个警告的话,临时改变诊断编译标记来抑制指定类型的警告



(4) -Wall和-Wextra
(5) Treat Warnings as Errors来开启,或者加入-Werror标识。

NS_ENUM & NS_OPTIONS
位掩码用NS_OPTIONS宏,语法和NS_ENUM完全相同,但这个宏提示编译器值是如何通过位掩码|组合在一起的。

《SDWebImage缓存图片的机制》

How is SDWebImage better than X? - NSURLCache,NSCache,SDWebImageDecoder,AFNetworking相同

<1> 从iOS 5,NSURLCache开始处理磁盘缓存,SDWebImage比一般的NSURLRequest的优势在哪儿?

iOS NSURLCache对原生的HTTP响应做内存和磁盘缓存(从iOS 5)。对于图片的缓存实际应用的是NSURLCache自带的cache机制。而NSURLCache每次都要把缓存的raw data 再转化为UIImage,这带来了额外的操作,如数据解析(编码HTTP数据),内存copy等等。

i. 另一方面,SDWebImage缓存图片呈现到内存中,并且存储原始压缩(但解码)的图片文件到磁盘。UIImage用NSCache存储在内存中,因此不涉及copy,并且当app或者系统需要的时候,内存会被尽快释放。

ii. 此外,图片解压一般发生在主线程,你第一次在UIImageView上用UIImage时,这样主线程会有延迟。解压过程会被强制在后台线程通过SDWebImageDecoder操作。

iii. 最后,SDWebImage完全忽视复杂且常误配HTTP缓存控制协议。这极大的加速了缓存的查找。

<2> AFNetworking提供了相似功能的UIImageView,SDWebImage还有用吗?

当然有用,AFNetworking的优势是Foundation URL通过NSURLCache加载系统缓存,也有一个可配置的内存缓存用于UIImageView和UIButton,默认用NSCache。缓存行为能进一步制定,通过NSURLRequest对应的缓存策略。其他SDWebImage特性,像后台图片数据的解压,AFNetworking也有。

如果你已经用了AFNetworking且仅仅需要一个简易的异步图片加载分类,其内置的UIKit extensions应该能满足你的需求。

移动端图片格式调研

JPEG 是目前最常见的图片格式,它只支持有损压缩,其压缩算法可以精确控制压缩比,以图像质量换得存储空间。许多移动设备的 CPU 都支持针对它的硬编码硬解码

PNG 本身的设计目的是替代 GIF 格式,所以它与 GIF 有更多相似的地方。PNG 只支持无损压缩,所以它的压缩比是有上限的。相对于 JPEG 和 GIF 来说,它最大的优势在于支持完整的透明(alpha)通道。..

WebP(lossless, quality(75), method(4) - use_threads, bypass_filtering, no_fancy_upsampling) Google 的图片格式,希望以更高的压缩比替代 JPEG。它用 VP8 视频帧内编码作为其算法基础,取得了不错的压缩效果。它支持有损无损压缩、支持完整的透明通道、也支持多帧动画,并且没有版权问题,是一种非常理想的图片格式。

GIF 通常情况下只支持256种颜色、透明通道只有1bit、文件压缩比不高。它唯一的优势就是支持多帧动画。

iOS 底层是用 ImageIO.framework 实现的图片编解码。目前 iOS 原生支持的格式有:JPEG、JPEG2000、PNG、GIF、BMP、ICO、TIFF、PICT,自 iOS8.0起,ImageIO 又加入了 APNG、SVG、RAW 格式的支持。在上层,开发者可以直接调用 ImageIO 对上面这些图片格式进行编码和解码。对于动图来说,开发者可以解码动画 GIF 和 APNG、可以编码动画 GIF。



更高效的异步图片加载 -- CALayer

SDWebImage 在这个 Demo 里仍然会产生少量性能问题,并且有些地方不能满足我的需求,所以我自己实现了一个性能更高图片加载库。在显示简单的单张图片时,利用 UIView.layer.contents 就足够了,没必要使用 UIImageView 带来额外的资源消耗,为此我在 CALayer 上添加了 setImageWithURL 等方法。除此之外,我还把图片解码等操作通过 YYDispatchQueuePool进行管理,控制了 App 总线程数量。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339

推荐阅读更多精彩内容