【iOS】—— SDWebImage源码学习(2)(源码解读)

【iOS】—— SDWebImage源码学习(2)(源码解读)

  • 1.UIKit层
    • 取消当前正在进行的异步下载
    • 占位图策略
    • 判断url是否合法
    • 下载图片操作
  • 2. SDWebImageManager
    • 判断url是否合法
    • 判断已加载失败的url
    • 保存操作
    • 查找缓存
  • 3. SDWebImageDownloader
    • SDWebImageDownloader基本属性
    • SDWebImageDownloader枚举类型
    • 核心方法
  • 4. SDWebImageDownloaderOperation
    • 核心方法
    • 取消某个操作
    • 全部暂停或取消
  • 重点问题
    • 使用流程
    • SDWebImage 的内存警告是如何处理
    • 其他小问题

1.UIKit层

最外层的是UIImageView+WebCache,在这个层中为我们提供了很多借口。

- (void)sd_setImageWithURL:(nullable NSURL *)url {[self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder {[self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {[self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
}- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context {[self sd_setImageWithURL:url placeholderImage:placeholder options:options context:context progress:nil completed:nil];
}- (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock {[self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock];
}- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {[self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
}- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock {[self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock];
}
- (void)sd_setImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionscontext:(nullable SDWebImageContext *)contextprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nullable SDExternalCompletionBlock)completedBlock {[self sd_internalSetImageWithURL:urlplaceholderImage:placeholderoptions:optionscontext:contextsetImageBlock:nilprogress:progressBlockcompleted:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {if (completedBlock) {completedBlock(image, error, cacheType, imageURL);}}];
}
- (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDExternalCompletionBlock)completedBlock {NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url];UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:key];[self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock];    
}

这些最终都是调用下面这个全能方法。全能方法除了必需的的图片地址,还提供了占位图、可选项、加载进度和完成回调。

- (void)sd_setImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionsprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nullable SDExternalCompletionBlock)completedBlock;

这个全能方法并没有什么实际的实现,还是对另一个分类UIView+WebCache方法的封装,看看全能方法的实现:

- (void)sd_setImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionscontext:(nullable SDWebImageContext *)contextprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nullable SDExternalCompletionBlock)completedBlock {[self sd_internalSetImageWithURL:urlplaceholderImage:placeholderoptions:optionscontext:contextsetImageBlock:nilprogress:progressBlockcompleted:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {if (completedBlock) {completedBlock(image, error, cacheType, imageURL);}}];
}

上面的这个方法也调用了全能方法,下面是其核心实现:

- (void)sd_internalSetImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionscontext:(nullable SDWebImageContext *)contextsetImageBlock:(nullable SDSetImageBlock)setImageBlockprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock {if (context) {// copy to avoid mutable object// 复制以避免可变对象context = [context copy];} else {context = [NSDictionary dictionary];}// 生成一个有效的操作密钥NSString *validOperationKey = context[SDWebImageContextSetImageOperationKey];// 如果传入了参数就用传入的,否则就用当前类的类名if (!validOperationKey) {// pass through the operation key to downstream, which can used for tracing operation or image view class// 通过操作键传递到下游,可用于跟踪操作或图像视图类validOperationKey = NSStringFromClass([self class]);SDWebImageMutableContext *mutableContext = [context mutableCopy];mutableContext[SDWebImageContextSetImageOperationKey] = validOperationKey;context = [mutableContext copy];}self.sd_latestOperationKey = validOperationKey;[self sd_cancelImageLoadOperationWithKey:validOperationKey];self.sd_imageURL = url;// 如果没有选择延迟加载占位图if (!(options & SDWebImageDelayPlaceholder)) {// 在主线程主队列中设置占位图dispatch_main_async_safe(^{// 作为图片下载完成之前的替代图片[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];});}// 如果传入了图片链接if (url) {// reset the progress// 重置进度NSProgress *imageProgress = objc_getAssociatedObject(self, @selector(sd_imageProgress));// 获取图像加载进度if (imageProgress) {// 初始化图片加载进度imageProgress.totalUnitCount = 0;imageProgress.completedUnitCount = 0;}#if SD_UIKIT || SD_MAC// check and start image indicator// 是否显示进度条(小菊花)[self sd_startImageIndicator];id<SDWebImageIndicator> imageIndicator = self.sd_imageIndicator;
#endif// 生成图片管理者对象,如果context中有就用context中的SDWebImageManager *manager;SDWebImageManager *manager = context[SDWebImageContextCustomManager];// 否则就直接生成SDWebImageManager单例对象if (!manager) {manager = [SDWebImageManager sharedManager];} else {// remove this manager to avoid retain cycle (manger -> loader -> operation -> context -> manager)// 删除此管理器以避免保留周期(管理器 -> 加载程序 -> 操作 -> 上下文 -> 管理器)SDWebImageMutableContext *mutableContext = [context mutableCopy];mutableContext[SDWebImageContextCustomManager] = nil;context = [mutableContext copy];}// 生成一个代码块用来在下载图片的方法中监听进度并进行回调SDImageLoaderProgressBlock combinedProgressBlock = ^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {if (imageProgress) {imageProgress.totalUnitCount = expectedSize;imageProgress.completedUnitCount = receivedSize;}
#if SD_UIKIT || SD_MACif ([imageIndicator respondsToSelector:@selector(updateIndicatorProgress:)]) {double progress = 0;if (expectedSize != 0) {progress = (double)receivedSize / expectedSize;}progress = MAX(MIN(progress, 1), 0); // 0.0 - 1.0dispatch_async(dispatch_get_main_queue(), ^{[imageIndicator updateIndicatorProgress:progress];});}
#endifif (progressBlock) {progressBlock(receivedSize, expectedSize, targetURL);}};@weakify(self);// 生成图片操作对象,并开始下载图片id <SDWebImageOperation> operation = [manager loadImageWithURL:url options:options context:context progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {@strongify(self);if (!self) { return; }// if the progress not been updated, mark it to complete state// 如果已经完成并且没有错误,并且进度没有更新,就将进度状态设为未知if (imageProgress && finished && !error && imageProgress.totalUnitCount == 0 && imageProgress.completedUnitCount == 0) {imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown;imageProgress.completedUnitCount = SDWebImageProgressUnitCountUnknown;}#if SD_UIKIT || SD_MAC// check and stop image indicator// 检查和停止图像指示器(如果有加载小菊花的话就移除掉)if (finished) {[self sd_stopImageIndicator];}
#endif// 是否应该回调完成block: 如果已经完成或者设置了在设置图片前处理BOOL shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);// 是否应该不设置图片: 如果有图片但设置了在设置图片前处理,或者没有图片并且没有设置延迟加载占位图BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||(!image && !(options & SDWebImageDelayPlaceholder)));// 生成完成回调代码块SDWebImageNoParamsBlock callCompletedBlockClojure = ^{// 如果没有生成强引用的self就终止执行 if (!self) { return; }// 如果需要设置图片就直接刷新视图if (!shouldNotSetImage) {[self sd_setNeedsLayout];}// 如果传入了回调block并且应该进行回调,就直接回调if (completedBlock && shouldCallCompletedBlock) {completedBlock(image, data, error, cacheType, finished, url);}};// 如果不需要设置图片就在主线程主队列中调用上面生成的完成回调代码块,并且不再向下执行if (shouldNotSetImage) {dispatch_main_async_safe(callCompletedBlockClojure);return;}// 生成变量保存数据UIImage *targetImage = nil;NSData *targetData = nil;if (image) {// 如果图片下载成功就用变量保存图片targetImage = image;targetData = data;} else if (options & SDWebImageDelayPlaceholder) {// 如果图片下载失败并且设置了延迟加载占位图,就保存占位图targetImage = placeholder;targetData = nil;}#if SD_UIKIT || SD_MACSDWebImageTransition *transition = nil;BOOL shouldUseTransition = NO;if (options & SDWebImageForceTransition) {// AlwaysshouldUseTransition = YES;} else if (cacheType == SDImageCacheTypeNone) {// From networkshouldUseTransition = YES;} else {// From disk (and, user don't use sync query)if (cacheType == SDImageCacheTypeMemory) {shouldUseTransition = NO;} else if (cacheType == SDImageCacheTypeDisk) {// SDWebImageQueryMemoryDataSync:// 默认情况下,当您仅指定“SDWebImageQueryMemoryData”时,我们会异步查询内存映像数据。// 将此掩码也组合在一起,以同步查询内存图像数据// 不建议同步查询数据,除非您要确保在同一 runloop 中加载映像以避免在单元重用期间闪烁。// SDWebImageQueryDiskDataSync:// 默认情况下,当内存缓存未命中时,我们会异步查询磁盘缓存。此掩码可以强制同步查询磁盘缓存(当内存缓存未命中时)。// 这两个选项打开则NO。if (options & SDWebImageQueryMemoryDataSync || options & SDWebImageQueryDiskDataSync) {shouldUseTransition = NO;} else {shouldUseTransition = YES;}} else {// Not valid cache type, fallbackshouldUseTransition = NO;}}// 检查一下是否应该转换图片:如果下载完成,并且设置了图片强制转换或者图片缓存类型是不缓存直接从网络加载,就进行强制转换if (finished && shouldUseTransition) {transition = self.sd_imageTransition;}
#endifdispatch_main_async_safe(^{
#if SD_UIKIT || SD_MAC// 在主线程主队列中设置图片[self sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL];
#else// 如果用户没有设置调度组,就直接在主线程主队列中设置图片和调用完成回调代码块[self sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:cacheType imageURL:imageURL];
#endifcallCompletedBlockClojure();});}];// 根据密钥保存下载图片的操作[self sd_setImageLoadOperation:operation forKey:validOperationKey];} else {
#if SD_UIKIT || SD_MAC// 如果没传入图片链接,就在主线程主队列移除加载小菊花[self sd_stopImageIndicator];
#endifdispatch_main_async_safe(^{// 如果传入了完成回调block就回调错误信息if (completedBlock) {NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey : @"Image url is nil"}];completedBlock(nil, nil, error, SDImageCacheTypeNone, YES, url);}});}
}

接下来我们讨论一下这段代码的核心步骤。

取消当前正在进行的异步下载

取消当前正在进行的异步下载,确保每个UIImageView对象中永远只存在一个operation,当前只允许一个图片网络请求,该operation负责从缓存中获取image或者是重新下载image。

// 生成一个有效的操作密钥NSString *validOperationKey = context[SDWebImageContextSetImageOperationKey];// 如果传入了参数就用传入的,否则就用当前类的类名if (!validOperationKey) {validOperationKey = NSStringFromClass([self class]);SDWebImageMutableContext *mutableContext = [context mutableCopy];mutableContext[SDWebImageContextSetImageOperationKey] = validOperationKey;context = [mutableContext copy];}
// 取消先前下载的任务
[self sd_cancelImageLoadOperationWithKey:validOperationKey];... // 下载图片操作// 将生成的加载操作赋值给UIView的自定义属性
[self sd_setImageLoadOperation:operation forKey:validOperationKey];

上述方法定义在UIView+WebCacheOperation类中:

- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {if (key) {// Cancel in progress downloader from queue// 从队列中取消正在进行的下载程序// 获取添加在UIView的自定义属性SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];id<SDWebImageOperation> operation;@synchronized (self) {operation = [operationDictionary objectForKey:key];}if (operation) {// 实现了SDWebImageOperation的协议if ([operation respondsToSelector:@selector(cancel)]) {[operation cancel];}@synchronized (self) {[operationDictionary removeObjectForKey:key];}}}
}
- (void)sd_setImageLoadOperation:(nullable id<SDWebImageOperation>)operation forKey:(nullable NSString *)key {if (key) {// 如果之前已经有过该图片的下载操作,则取消之前的图片下载操作if (operation) {SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];@synchronized (self) {[operationDictionary setObject:operation forKey:key];}}}
}

实际上,所有的操作都是由一个实际上,所有的操作都是由一个operationDictionary字典维护的,执行新的操作之前,cancel所有的operation。
通俗来说:这两个方法的组合使用,可以实现对图片加载操作的管理。在设置新的图片加载操作之前,会先取消之前的操作,从而确保每个视图只执行最新的图片加载操作。 这样可以避免出现重复加载或并发加载的问题,同时也提高了图片加载的效率和用户体验。

  • 重复加载: 指的是在同一时间点或短时间内多次加载相同资源的情况。这可能是由于代码逻辑错误、用户操作或系统问题导致的。重复加载可能会造成资源浪费、性能下降和不必要的网络请求。
  • 并发加载: 指的是在同一时间点或短时间内同时进行多个加载操作的情况。这种情况通常发生在多线程或并发执行的环境下,多个加载操作可以同时进行,以提高效率和响应性。但是必须确保每个加载操作独立处理自己的资源,否则会出现数据竞争和冲突。

占位图策略

作为图片下载完成之前的替代图片。dispatch_main_async_safe是一个宏,保证在主线程安全执行。

if (!(options & SDWebImageDelayPlaceholder)) {// 在主线程主队列中设置占位图dispatch_main_async_safe(^{// 设置占位图[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];});
}

判断url是否合法

如果url合法,则进行图片下载操作,否则直接block回调失败。

if (url) {// 下载图片操作
} else {dispatch_main_async_safe(^{
#if SD_UIKIT[self sd_removeActivityIndicator];
#endifif (completedBlock) {NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];completedBlock(nil, error, SDImageCacheTypeNone, url);}});
}

下载图片操作

下载图片的操作是由SDWebImageManager完成的,它是一个单例。

- (nullable SDWebImageCombinedOperation *)loadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageOptions)optionscontext:(nullable SDWebImageContext *)contextprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nonnull SDInternalCompletionBlock)completedBlock;

下载完成之后刷新UIImageView的图片。

// 根据枚举类型,判断是否需要设置图片
shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);
BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||(!image && !(options & SDWebImageDelayPlaceholder)));
SDWebImageNoParamsBlock callCompletedBlockClojure = ^{if (!sself) { return; }if (!shouldNotSetImage) {// 设置图片[sself sd_setNeedsLayout];  }if (completedBlock && shouldCallCompletedBlock) {completedBlock(image, error, cacheType, url);}
};// 不要自动设置图片,则调用block传入image对象
if (shouldNotSetImage) {    dispatch_main_async_safe(callCompletedBlockClojure);return;
}// 设置图片操作
dispatch_main_async_safe(^{
#if SD_UIKIT || SD_MAC[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL];
#else[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock];
#endifcallCompletedBlockClojure();
});

最后,把返回的 operation 添加到operationDictionary中,方便后续的cancel。

// 将生成的加载操作赋值给UIView的自定义属性
[self sd_setImageLoadOperation:operation forKey:validOperationKey];

2. SDWebImageManager

SDWebImageManager的官方介绍:

  • SDWebImageManager是UIImageView+WebCache类别背后的类。
  • 它将异步下载程序(SDWebImageDownloader)与图像缓存存储(SDImageCache)绑定。
  • 您可以直接使用这个类,在另一个地方中使用缓存下载web图像,而不是一个UIView。

SDWebImageManager是隐藏在UIImageView+WebCache之后,用于处理异步下载和图片缓存的类。同时你也可以直接使用SDWebImageManager的如下方法来直接下载图片。

- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock;

下面我们看看SDWebImageManager.h里的内容。
定义了一些枚举类型的SDWebImageOptions。

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions ) {SDWebImageRetryFailed = 1 < < 0,SDWebImageLowPriority = 1 < < 1,SDWebImageCacheMemoryOnly = 1 < < 2,SDWebImageProgressiveDownload = 1 < < 3,SDWebImageRefreshCached = 1 < < 4,SDWebImageContinueInBackground = 1 < < 5,SDWebImageHandleCookies = 1 < < 6,SDWebImageAllowInvalidSSLCertificates = 1 < < 7,SDWebImageHighPriority = 1 < < 8,SDWebImageDelayPlaceholder = 1 < < 9,SDWebImageTransformAnimatedImage = 1 < < 10,SDWebImageAvoidAutoSetImage = 1 < < 11,SDWebImageScaleDownLargeImages = 1 < < 12,
};

表达了图片加载过程中对于图片的一些状态,如SDWebImageRetryFailed表示即使某个url下载失败了,SDWebImage还是会尝试再次下载它;SDWebImageLowPriority表示禁止图片在交互发生的时候下载(如滑动tableview)等等。

接下来声明了四个block。

// 操作完成的回调,被上层的扩展调用。
typedef void(^SDWebImageCompletionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL);// 被SDWebImageManager调用。
// 如果使用了SDWebImageProgressiveDownload标记,这个block可能会被重复调用,直到图片完全下载结束,
// finished=true,再最后调用一次这个block。
typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL);// SDWebImageManager每次把URL转换为cache key的时候调用,可以删除一些image URL中的动态部分。
typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url);typedef NSData * _Nullable(^SDWebImageCacheSerializerBlock)(UIImage * _Nonnull image, NSData * _Nullable data, NSURL * _Nullable imageURL);

然后定义了SDWebImageManagerDelegate协议。

@protocol SDWebImageManagerDelegate @optional
// 控制在cache中没有找到image时 是否应该去下载。
- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;// 在下载之后,缓存之前转换图片。在全局队列中操作,不阻塞主线程
- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL;@end

SDWebImageManager是单例使用的,分别维护了一个SDImageCache实例和一个SDWebImageDownloader实例。其中包含一些方法:

// 初始化SDWebImageManager单例,在init方法中已经初始化了cache单例和downloader单例。
- (nonnull instancetype)initWithCache:(nonnull id<SDImageCache>)cache loader:(nonnull id<SDImageLoader>)loader NS_DESIGNATED_INITIALIZER;
// 下载图片
- (id )downloadImageWithURL:(NSURL *)urloptions:(SDWebImageOptions)optionsprogress:(SDWebImageDownloaderProgressBlock)progressBlockcompleted:(SDWebImageCompletionWithFinishedBlock)completedBlock;
// 缓存给定URL的图片
- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url;
// 取消当前所有的操作
- (void)cancelAll;
// 监测当前是否有进行中的操作
- (BOOL)isRunning;
// 监测图片是否在缓存中, 先在memory cache里面找  再到disk cache里面找
- (BOOL)cachedImageExistsForURL:(NSURL *)url;
// 监测图片是否缓存在disk里
- (BOOL)diskImageExistsForURL:(NSURL *)url;
// 监测图片是否在缓存中,监测结束后调用completionBlock
- (void)cachedImageExistsForURL:(NSURL *)urlcompletion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
// 监测图片是否缓存在disk里,监测结束后调用completionBlock
- (void)diskImageExistsForURL:(NSURL *)urlcompletion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
//返回给定URL的cache key
- (NSString *)cacheKeyForURL:(NSURL *)url;

下面我们看看下载图片的主要过程,也就是下面这个方法:

- (nullable SDWebImageCombinedOperation *)loadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageOptions)optionscontext:(nullable SDWebImageContext *)contextprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nonnull SDInternalCompletionBlock)completedBlock;

判断url是否合法

if ([url isKindOfClass:NSString.class]) {url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
// 防止应用程序在参数类型错误(如发送NSNull而不是NSURL)时崩溃
if (![url isKindOfClass:NSURL.class]) {url = nil;
}

这里为了防止很多用户直接传递NSString,先将NSString类型url转换成NSURL类型,然后防止一下程序因参数类型错误导致的crash。

判断已加载失败的url

BOOL isFailedUrl = NO;
if (url) {  // 判断url是否是加载失败过的urlLOCK(self.failedURLsLock);isFailedUrl = [self.failedURLs containsObject:url];UNLOCK(self.failedURLsLock);
}
// 如果url为空或者url下载失败并且设置了不再重试
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];return operation;
}

这里使用了集合failedURLs,用于保存之前加载失败的urls。如果url为空或者url之前失败过且有SDWebImageRetryFailed枚举类型,直接调用completedBlock返回错误。

保存操作

LOCK(self.runningOperationsLock);
[self.runningOperations addObject:operation];
UNLOCK(self.runningOperationsLock);

使用可变数组runningOperations,用于保存所有的operation,并且监测是否有operation在执行,即判断running状态。

查找缓存

SDWebImageManager会首先在内存缓存和磁盘缓存的 cache 中查找是否下载过相同的照片,即调用imageCache的下面方法:

- (nullable SDImageCacheToken *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDImageCacheQueryCompletionBlock)doneBlock;

如果操作取消,则直接返回:

__strong __typeof(weakOperation) strongOperation = weakOperation;
// operation取消,那么将下载任务从下载队列中直接移除
if (!strongOperation || strongOperation.isCancelled) {[self safelyRemoveOperationFromRunning:strongOperation];return;
}

如果没有在缓存中找到图片,或者不管是否找到图片,只要operation有SDWebImageRefreshCached标记,那么若SDWebImageManagerDelegate的shouldDownloadImageForURL方法返回 true,即允许下载时,都使用 imageDownloader的下载方法:

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageDownloaderOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;

如果下载有错误,直接调用completedBlock返回错误,并且视情况将url添加到failedURLs里面:

dispatch_main_sync_safe(^{// 下载有误,直接调用completedBlock返回错误if (strongOperation && !strongOperation.isCancelled) {completedBlock(nil, error, SDImageCacheTypeNone, finished, url);}
});
//视error情况将url添加到failedURLs
if (error.code != NSURLErrorNotConnectedToInternet&& error.code != NSURLErrorCancelled&& error.code != NSURLErrorTimedOut&& error.code != NSURLErrorInternationalRoamingOff&& error.code != NSURLErrorDataNotAllowed&& error.code != NSURLErrorCannotFindHost&& error.code != NSURLErrorCannotConnectToHost) {@synchronized (self.failedURLs) {[self.failedURLs addObject:url];}
}

如果下载成功,若支持失败重试,将url从failURLs里删除:

if ((options & SDWebImageRetryFailed)) {@synchronized (self.failedURLs) {[self.failedURLs removeObject:url];}
}

如果delegate中实现了imageManager:transformDownloadedImage:withURL:方法,图片在缓存之前,需要做转换(在全局队列中调用,不阻塞主线程)。转化成功后下载全部结束,图片存入缓存,调用completedBlock回调。其中第一个参数是转换后的image。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];if (transformedImage && finished) {BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];//缓存图片[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];}dispatch_main_sync_safe(^{if (strongOperation && !strongOperation.isCancelled) {completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);}});
});

存入缓存都是调用imageCache的下面方法:

- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk;

如果没有在缓存找到图片,且不允许下载,直接调用completedBlock,第一个参数为nil。

dispatch_main_sync_safe(^{__strong __typeof(weakOperation) strongOperation = weakOperation;if (strongOperation && !weakOperation.isCancelled) {completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);}
});

最后将这个operation从runningOperations里删除。

@synchronized (self.runningOperations) {[self.runningOperations removeObject:operation];}

可以看到,SDWebImageManger负责处理和协调SDWebImageDownloader和SDWebImageCache,并与 UIKit层进行交互(传入图片)。

3. SDWebImageDownloader

SDWebImage的下载模块主要包括两个类:SDWebImageDownloader和SDWebImageDownloaderOperation。

SDWebImageDownloader负责对所有下载任务的管理,为下载图片提供专用和优化的异步下载器。

SDWebImageDownloaderOperation负责具体的一个下载任务的执行,且为了拓展下载功能,还支持实现SDWebImageDownloaderOperationInterface协议来自定义具体下载。

这里我们先看看SDWebImageDownloader。

SDWebImageDownloader基本属性

  • currentDownloadCount:显示当前仍然需要下载的大小
  • downloadTimeout:下载操作的超时时间(单位为秒)。默认值:15.0
  • executionOrder:修改下载操作的执行顺序。默认值为SDWebImageDownloaderFIFOExecutionOrder
  • headerFilter:设置过滤器用来挑选下载图片的HTTP请求的头。这个块在每个图片下载请求时被调用,返回在相应的HTTP请求中用于HTTP头的NSDictionary
  • maxConcurrentDownloads:最大的并发下载数
  • username:设置用户名
  • password:设置密码
  • sessionConfiguration:NSURLSession内部使用的配置
  • shouldDecompressImages:默认为YES,解压已经下载和缓存的图片可以提高性能,但是会消耗很多的内容。如果你遇到由于大量的消耗内存导致崩溃,建议设置为NO
  • urlCredential:为请求操作设置默认的URL证书
//显示当前仍然需要下载的大小
@property (readonly, nonatomic) NSUInteger currentDownloadCount;//下载操作的超时时间(单位为秒)。默认值:15.0
@property (assign, nonatomic) NSTimeInterval downloadTimeout;//修改下载操作的执行顺序。默认值为SDWebImageDownloaderFIFOExecutionOrder
@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;//设置过滤器用来挑选下载图片的HTTP请求的头。这个块在每个图片下载请求时被调用,返回在相应的HTTP请求中用于HTTP头的NSDictionary
@property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter;//最大的并发下载数
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;//设置用户名
@property (strong, nonatomic, nullable) NSString *username;//设置密码
@property (strong, nonatomic, nullable) NSString *password;//NSURLSession内部使用的配置
@property (readonly, nonatomic, nonnull) NSURLSessionConfiguration *sessionConfiguration;//默认为YES,解压已经下载和缓存的图片可以提高性能,但是会消耗很多的内容。如果你遇到由于大量的消耗内存导致崩溃,建议设置为NO
@property (assign, nonatomic) BOOL shouldDecompressImages;//为请求操作设置默认的URL证书
@property (strong, nonatomic, nullable) NSURLCredential *urlCredential;

SDWebImageDownloader枚举类型

SDWebImageDownloader枚举类型提供了很多下载选项,可以根据情况进行配置。如设置下载优先级、进度、后台下载,图片缩放等,同时支持先进先出,先进后出的下载方式。

typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {//下载低优先级SDWebImageDownloaderLowPriority = 1 << 0,//下载高优先级SDWebImageDownloaderHighPriority = 1 << 7,// 带有进度SDWebImageDownloaderProgressiveDownload = 1 << 1,//默认不使用URLCacheSDWebImageDownloaderUseNSURLCache = 1 << 2,//如果图片是在NSURLCAche中读取时,调用completion block时,返回nil,配合SDWebImageDownloaderUseNSURLCache使用SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,//支持后台下载SDWebImageDownloaderContinueInBackground = 1 << 4,//支持NSHTTPCookieStore的cookie信息,进而设置NSMutableURLRequest.HTTPShouldHandleCookies=YESSDWebImageDownloaderHandleCookies = 1 << 5,//允许不信任SSL证书SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,//缩放大图片SDWebImageDownloaderScaleDownLargeImages = 1 << 8,
};typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {//先进先出SDWebImageDownloaderFIFOExecutionOrder,//先进后出SDWebImageDownloaderLIFOExecutionOrder
};

核心方法

先看方法:

- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock andCompletedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback {// The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.//如果URL为空,直接执行完成回调block并传入nil参数,结束本次请求if (url == nil) {if (completedBlock != nil) {completedBlock(nil, nil, nil, NO);}return;}//NSMutableDicitonary不是线程安全的,利用GCD的barrier 保证线程安全,确保字典不会同时存取dispatch_barrier_sync(self.barrierQueue, ^{BOOL first = NO;if (!self.URLCallbacks[url]) {self.URLCallbacks[url] = [NSMutableArray new];first = YES;}// Handle single download of simultaneous download request for the same URL//为URL创建一个唯一对应的callbacks,并赋值给self.URLCallBacksNSMutableArray *callbacksForURL = self.URLCallbacks[url];NSMutableDictionary *callbacks = [NSMutableDictionary new];if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];[callbacksForURL addObject:callbacks];self.URLCallbacks[url] = callbacksForURL;if (first) {createCallback();}});
}

URLCallBacks字典存储的每个请求的callbacksForURL,走完上面这个函数,我们就能确保每个请求都能和它的progressBlock和completedBlock回调一一对应。

4. SDWebImageDownloaderOperation

前面说过,SDWebImageDownloaderOperation负责下载任务的具体实现,我们直接看核心方法。

核心方法

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageDownloaderOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {__weak SDWebImageDownloader *wself = self;return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{__strong __typeof (wself) sself = wself;// 设置超时时间NSTimeInterval timeoutInterval = sself.downloadTimeout;if (timeoutInterval == 0.0) {timeoutInterval = 15.0;}// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise// 创建request,针对不同缓存策略不同处理NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);request.HTTPShouldUsePipelining = YES;// 设置请求头部if (sself.headersFilter) {request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);}else {request.allHTTPHeaderFields = sself.HTTPHeaders;}// 创建操作对象SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];operation.shouldDecompressImages = sself.shouldDecompressImages;// 给操作对象设置urlCredentialif (sself.urlCredential) {operation.credential = sself.urlCredential;} else if (sself.username && sself.password) {operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];}// 设置操作级别if (options & SDWebImageDownloaderHighPriority) {operation.queuePriority = NSOperationQueuePriorityHigh;} else if (options & SDWebImageDownloaderLowPriority) {operation.queuePriority = NSOperationQueuePriorityLow;}// 把操作添加到队列[sself.downloadQueue addOperation:operation];// 根据executionOrder设置,设置依赖if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {// Emulate LIFO execution order by systematically adding new operations as last operation's dependency// 模拟后进先出执行顺序[sself.lastAddedOperation addDependency:operation];sself.lastAddedOperation = operation;}return operation;}];
}

取消某个操作

- (void)cancel:(nullable SDWebImageDownloadToken *)token {dispatch_barrier_async(self.barrierQueue, ^{SDWebImageDownloaderOperation *operation = self.URLOperations[token.url];BOOL canceled = [operation cancel:token.downloadOperationCancelToken];if (canceled) {[self.URLOperations removeObjectForKey:token.url];}});
}

全部暂停或取消

- (void)setSuspended:(BOOL)suspended {(self.downloadQueue).suspended = suspended;
}- (void)cancelAllDownloads {[self.downloadQueue cancelAllOperations];
}

重点问题

使用流程

  1. 入口setImageWithURL:placeholderImage:options:先显示预览图placeholderImage,然后 SDWebImageManager 根据URL开始处理图片
  2. SDWebImageManager调用downloadWithURL:delegate:options:userInfo:方法,进入SDImageCache的queryDiskCacheForKey:delegate:userInfo:方法,从缓存查找图片是否已经下载
  3. 先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate回调imageCache:didFindImage:forKey:userInfo:到SDWebImageManager
  4. SDWebImageManagerDelegate回调webImageManager:didFinishWithImage:方法,以在UIImageView+WebCache,UIButton+WebCache等前端展示图片
  5. 如果内存缓存中没有,生成NSInvocationOperation添加到队列开始从硬盘查找图片是否已经缓存
  6. 根据URLKey在硬盘缓存目录下尝试读取图片文件。这一步在NSOperation进行,所以要回主线程进行结果回调:使用notifyDelegate:方法
  7. 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate回调imageCache:didFindImage:forKey:userInfo:,进而回调展示图片
  8. 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调imageCache:didNotFindImageForKey:userInfo:
  9. 共享或重新生成一个下载器SDWebImageDownloader开始下载图片。
  10. 图片下载由NSURLConnection来执行,实现相关delegate来判断图片下载中、下载完成和下载失败。
  11. connection:didReceiveData:中利用ImageIO提供了加载效果,展示图片下载进度。
  12. connectionDidFinishLoading:数据下载完成后交给SDWebImageDecoder做图片解码处理。
  13. 图片解码处理在一个NSOperationQueue完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
  14. 在主线程notifyDelegateOnMainThreadWithInfo:宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo:回调给SDWebImageDownloader。
  15. imageDownloader:didFinishWithImage:回调给SDWebImageManager告知图片下载完成。
  16. 通知所有的downloadDelegates下载完成,回调给需要的地方展示图片。
  17. 将图片保存到SDImageCache中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独NSInvocationOperation完成,避免拖慢主线程。
  18. SDImageCache在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
  19. SDWebImagePrefetcher可以预先下载图片,方便后续使用。

SDWebImage 的内存警告是如何处理

利用通知中心观察:

  • UIApplicationDidReceiveMemoryWarningNotification 监听内存警告通知,执行 clearMemory 方法,清理内存缓存。
  • UIApplicationWillTerminateNotification 监听应用程序将要终止通知,执行 cleanDisk 方法,清理磁盘缓存。
  • UIApplicationDidEnterBackgroundNotification 监听应用程序进入后台通知,执行 backgroundCleanDisk 方法,后台清理磁盘。

通过以上通知监听,能够保证缓存文件的大小始终在控制范围之内。clearDisk 清空磁盘缓存表示:将所有缓存目录中的文件全部删除。实际情况中会将缓存目录直接删除,再次创建一个同名空目录。

其他小问题

  • Q:图片文件缓存的时间有多长?
    A:1周。static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7;
  • Q:SDWebImage 的内存缓存是用什么实现的?
    A:NSCache。SDImageCache类中有一个NSCache类型的memCache属性,用这个属性来进行内存缓存。
    存缓存:[self.memCache setObject:image forKey:key cost:cost];
    取缓存:return [imageFromMemoryCacheForKey:key];
  • Q:SDWebImage 的最大并发数是多少?
    A:maxConcurrentDownloads = 6为程序固定死了,其实可以通过属性进行调整。
  • Q:SDWebImage是如何区分不同格式的图像?
    A:根据图像数据第一个字节来判断的。
  • Q:SDWebImage 缓存图片的名称是怎么确定的?
    A:使用文件名保存,重名的几率很高,因此使用 MD5 的散列函数。对完整的 URL 进行 md5,结果是一个 32 个字符长度的字符串。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/824248.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++类和对象】拷贝构造与赋值运算符重载

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

分布式搭载博客网站

一.运行环境&#xff1a; IP主机名系统服务192.168.118.128Server-WebLinuxWeb192.168.118.131Server-NFS-DNSLinuxNFS/DNS 二.基础配置 1. 配置主机名&#xff0c;hosts映射 [rootserver ~]# hostnamectl set-hostname Server-Web [rootserver ~]# hostname Server-Web [r…

【学习笔记】Python大数据处理与分析——数据预处理

一、数据清洗 1、唯一值与重复值 获取唯一值的方法是采用unique()函数&#xff0c;用于Series对象&#xff1a; s1 pd.Series([2, 3, 4, 1, 2, 5, 3, 6, 4, 9, 5, 3, 4, 2, 1, 2])print(s1.unique()) →[2 3 4 1 5 6 9] 但unique()函数不能用于DataFrame对象&#xff0c;而d…

Word分节后,页码不连续、转PDF每节后多出空白页解决办法

1. 问题图例 废话少说&#xff0c;先上图&#xff1a; 2. 问题分析 问题分析&#xff1a;出现以上问题的原因可能有&#xff0c; 未链接到上一节页面布局中节的起始位置设置为[奇数页] 3. 解决问题 若为【1. 未链接到上一节】导致该问题出现&#xff0c;则我们需要选中页脚…

Chatgpt掘金之旅—有爱AI商业实战篇|品牌故事业务|(十六)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、AI技术创业在品牌故事业务有哪些机会&#xff1f; 人工智能&#xff08;AI&#xff09;技术作为当今科技创新的前沿领域&#xff0c;为创业者提供了广阔的机会和挑战。随…

接口压力测试 jmeter--入门篇(一)

一 压力测试的目的 评估系统的能力识别系统的弱点&#xff1a;瓶颈/弱点检查系统的隐藏的问题检验系统的稳定性和可靠性 二 性能测试指标以及测算 【虚拟用户数】&#xff1a;线程用户【并发数】&#xff1a;指在某一时间&#xff0c;一定数量的虚拟用户同时对系统的某个功…

OpenMesh 网格平均曲率计算

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 根据 Laplace-Beltrami 算子与平均曲率法向的关系: 又根据余切 Laplace-Beltrami 算子的定义: 其中 Ai 为该点邻域面积,取 Voronoi cell 面积如下: 得到

PACNet CellNet(代码开源)|bulk数据作细胞分类,评估细胞命运性能的一大利器

文章目录 1.前言2.CellNet2.1CellNet简介2.2CellNet结果 3.PACNet3.1安装R包与加载R包3.2加载数据3.3开始训练和分类3.4可视化分类过程3.5可视化分类结果 4.细胞命运分类和免疫浸润比较 1.前言 今天冲浪看到一个细胞分类性能评估的R包——PACNet&#xff0c;它与转录组分析方法…

Prometheus + Grafana 搭建监控仪表盘

目标要求 1、需要展现的仪表盘&#xff1a; SpringBoot或JVM仪表盘 Centos物理机服务器&#xff08;实际为物理分割的虚拟服务器&#xff09;仪表盘 2、展现要求: 探索Prometheus Grafana搭建起来的展示效果&#xff0c;尽可能展示能展示的部分。 一、下载软件包 监控系统核心…

Spring Cloud Gateway集成聚合型Spring Boot API发布组件knife4j,增强Swagger

大家都知道&#xff0c;在前后端分离开发的时代&#xff0c;前后端接口对接是一项必不可少的工作。 可是&#xff0c;作为后端开发&#xff0c;怎么和前端更好的配合&#xff0c;才能让自己不心累、脑累&#xff0c;直接扔给前端一个后端开放api接口文档或者页面&#xff0c;让…

Unity之OpenXR+XR Interaction Toolkit快速监听手柄任意按键事件

前言 当我们开发一个VR时,有时希望监听一个手柄按键的点击事件,或者一个按钮的Value值等。但是每次有可能监听的按钮有不一样,有可能监听的值不一样,那么每次这么折腾,有点累了,难道就没有一个万能的方法,让我可以直接监听我想要的某个按钮的事件么? 答案是肯定的,今…

分类算法——朴素贝叶斯(四)

概率基础 1概率定义 概率定义为一件事情发生的可能性 扔出一个硬币&#xff0c;结果头像朝上 P(X)&#xff1a;取值在[0&#xff0c;1] 2女神是否喜欢计算案例 在讲这两个概率之前我们通过一个例子&#xff0c;来计算一些结果&#xff1a; 问题如下&#xff1a; 1、女神喜欢…

sql知识总结二

一.报错注入 1.什么是报错注入&#xff1f; 这是一种页面响应形式&#xff0c;响应过程如下&#xff1a; 用户在前台页面输入检索内容----->后台将前台输入的检索内容无加区别的拼接成sql语句&#xff0c;送给数据库执行------>数据库将执行的结果返回给后台&#xff…

2024第十五届蓝桥杯JavaB组省赛部分题目

目录 第三题 第四题 第五题 第六题 第七题 第八题 转载请声明出处&#xff0c;谢谢&#xff01; 填空题暂时可以移步另一篇文章&#xff1a;2024第十五届蓝桥杯 Java B组 填空题-CSDN博客 第三题 第四题 第五题 第六题 第七题 第八题 制作不易&#xff0c;还请点个赞支持…

数据结构-栈和队列刷题集(长期更新)

文章目录 万能计算器的实现以及源码分析1. leetcode 150 逆波兰表达式求值 万能计算器的实现以及源码分析 /*** 我们尝试写一个完整版的计算器,由于计算机不能很好的识别括号,所以一般要转换为逆波兰表达式求解* 思路解析 :* 1. 输入一个 中缀表达式* 2. 中缀表达式转化为list…

SpringBoot基于RabbitMQ实现消息可靠性

文章目录 1. ☃️概述2. ☃️生产者消息确认2.1 ❄️❄️概述2.2 ❄️❄️实战⛷️⛷️⛷️2.2.1 修改配置⛷️⛷️⛷️2.2.2 定义 Return 回调⛷️⛷️⛷️2.2.3 定义ConfirmCallback 3. ☃️消息持久化3.1 ❄️❄️交换机持久化3.2 ❄️❄️队列持久化3.3 ❄️❄️消息持久化…

进程、线程和协程

进程、线程和协程 进程是程序的执行实例 线程是进程的执行路径 协程是基于线程之上但又比线程更加轻量级的存在 进程与线程的区别 线程是程序执行的最小单位&#xff0c;而进程是操作系统分配资源的最小单位 进程和程序的区别 程序&#xff1a;执行特定任务的一串代码&a…

牛客Linux高并发服务器开发学习第二天

Gcc编译 利用gcc 生成应用时如果不加-o 和应用名&#xff0c;默认生成a.out 可以用./ a.out打开 Gcc工作流程 可执行程序Windows系统中为.exe Linux系统中为.out g也可以编辑c程序 gcc也可以编译cpp代码&#xff0c;只是在编译阶段gcc不能自动共和C程序使用的库进行联接&…

JS-43-Node.js02-安装Node.js和npm

Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;可以让JavaScript实现后端开发&#xff0c;所以&#xff0c;首先在本机安装Node.js环境。 一、安装Node.js 官网&#xff1a;下载 Node.js 默认两个版本的下载&#xff1a; 64位windows系统的LTS(Long Tim…

CST电磁仿真物体表面的Sheet结构和生成3D Model【基础教程】

由Sheet结构生成3D Model 使用Shell Solid and Thicken Sheet&#xff01; Modeling > Tools > Shape Tools > Shell Solid or Thicken Sheet Shell Solidor ThickenSheet会根据不同类型的模型提供两种完全不同的功能。 如033.由3D Model生成Cavity 所述&#xff…