【iOS】SDWebImage源码学习
SDWebImage源码学习
文章目录
- SDWebImage源码学习
- 前言
- SDWebImage缓存流程
- sd_setImageWithURL(UIImageView+WebCache层)
- sd_internalSetImageWithURL(UIView+WebCache层)
- loadImageWithURL(SDWebManger层)
- queryCacheOperationForKey(SDImageCache层)
- 删除缓存
- callDownloadProcessForOperation(SDWebImagerManger层)
- downloadImageWithURL(SDImageLoadersManager层)
- storeImage(SDImageCache)
- setImage(SDWebImageManager)
前言
SDWebImage是一个开源的第三方库,它提供了UIImageView的一个分类,以支持从远程服务器下载并缓存图片的功能。
SDWebImage这个第三方库有以下几个优势:
- 一个异步的图片加载器。
- 一个异步的内存+磁盘图片缓存
- 支持GIF、WebP图片
- 后台图片解压缩处理
- 确保同一个URL的图片不被多次下载
- 确保非法的URL不会被反复加载
- 确保下载及缓存时,主线程不被阻塞.
首先我们先看一下有关于SDWebImage官方给出的一个图:
下面我们主要讲一下这几个比较重要的对象
-
UIImageView (WebCache)类别,入口封装,实现读取图片完成后的回调
-
SDWebImageManager,对图片进行管理的中转站,记录哪些图片正在读取
- 向下层读取Cache(调用SDImageCache),或者向网络读取对象(调用SDWebImageDownloader)。
- 实现SDImageCache和SDWebImageDownloader的回调
-
SDImageCache
- 根据URL的MD5摘要对图片进行存储和读取(实现存在内存中或者存在硬盘上两种实现)
- 实现图片和内存清理工作
-
SDWebImageDownloader,根据URL向网络读取数据(实现部分读取和全部读取后再通知回调两种方式)
SDWebImage缓存流程
SDWebImage缓存流程
这里我们先看一下流程图:
这时候我们在用文字讲解一下流程:
sd_setImageWithURL(UIImageView+WebCache层)
- 现在某一个对象上的
SDWebImage
这个库中调用sd_setImageWithURL
这个方法,然后我们点进去看一下他的一个具体调用:
这里发现它是一个逐级调用一个完整的set方法,所以所有方法最后都会调用下面这个方法:
- (nullable id<SDWebImageOperation>)sd_internalSetImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionscontext:(nullable SDWebImageContext *)contextsetImageBlock:(nullable SDSetImageBlock)setImageBlockprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock;
这时候我们在看这个函数的一个逻辑:
sd_internalSetImageWithURL(UIView+WebCache层)
-
处理了参数的安全性质:
- 将
NSString
类型的 URL 转换为NSURL
。 - 过滤非法 URL(如
NSNull
),确保后续流程安全。 - 生成唯一的
validOperationKey
(默认为当前类名),用于标识和管理当前图片加载操作。(会将url作为属性绑定到UIView上)
if ([url isKindOfClass:NSString.class]) {url = [NSURL URLWithString:(NSString *)url];//SDWebImagey允许传入一个}// Prevents app crashing on argument type error like sending NSNull instead of NSURLif (![url isKindOfClass:NSURL.class]) {url = nil;}//前面处理一个参数的安全性质if (context) {// copy to avoid mutable object//创建一个副本避免直接修改原来的值context = [context copy];} else {context = [NSDictionary dictionary];}NSString *validOperationKey = context[SDWebImageContextSetImageOperationKey]; // 生成一个唯一的key来管理当前图片加载操作 后面也会将url作为属性绑定到UIView上
- 将
-
取消历史操作,保证一个单线程
NSString *validOperationKey = context[SDWebImageContextSetImageOperationKey]; //获得对应的一个keyif (!validOperationKey) { //当 validOperationKey 为空时(即用户未指定),进入 if 块生成默认key// pass through the operation key to downstream, which can used for tracing operation or image view class为了不直接修改传入的上下文对象(这可能会影响其他地方的使用),首先对其进行深复制,得到一个可修改的副本mutableContext。然后在这个副本中设置新的操作键(无论是用户自定义的还是当前类名)。最后,将修改后的可变上下文(mutableContext)再次复制成一个不可变字典,替换原先的context对象,以供后续操作使用。validOperationKey = NSStringFromClass([self class]);SDWebImageMutableContext *mutableContext = [context mutableCopy];mutableContext[SDWebImageContextSetImageOperationKey] = validOperationKey;context = [mutableContext copy];}// 默认情况下,如果没有设置SDWebImageAvoidAutoCancelImage选项,则取消与当前设置图片操作键相关联的所有先前的下载操作。self.sd_latestOperationKey = validOperationKey; //这一行的点语法在调用关联对象函数if (!(SD_OPTIONS_CONTAINS(options, SDWebImageAvoidAutoCancelImage))) { // 判断选项// cancel previous loading for the same set-image operation key by default[self sd_cancelImageLoadOperationWithKey:validOperationKey];} // 在 UI 开发中,尤其是在列表或滚动视图中,用户可能快速滚动,视图复用机制会导致视图的内容需要频繁更新。如果不取消先前的下载操作,就可能出现以下问题: // 性能问题:同时进行多个不必要的下载任务,增加内存和网络的负担。 // 数据错误:旧的下载任务可能后于新的任务完成,导致视图上显示的图片是错误的。- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {if (!key) {key = NSStringFromClass(self.class); // 如果key为nil就用类生成一个key}// Cancel in progress downloader from queue//取消任务SDOperationsDictionary *operationDictionary = [self sd_operationDictionary]; //获得所有的一个OperationsDictionaryid<SDWebImageOperation> operation;@synchronized (self) { //互斥锁保证他的一个安全性operation = [operationDictionary objectForKey:key];}if (operation) {if ([operation respondsToSelector:@selector(cancel)]) { //有这个方法就移除[operation cancel];}@synchronized (self) {[operationDictionary removeObjectForKey:key]; //移除一个队列字典中的队列}} }
-
初始化一个manger
SDWebImageManager *manager = context[SDWebImageContextCustomManager]; //从上下文中获取,如果没有就创建一个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];}
-
判断是否需要一个弱缓存,并且根据placeholder显示图片
BOOL shouldUseWeakCache = NO;if ([manager.imageCache isKindOfClass:SDImageCache.class]) {shouldUseWeakCache = ((SDImageCache *)manager.imageCache).config.shouldUseWeakMemoryCache;}if (!(options & SDWebImageDelayPlaceholder)) { //判断是否需要一个占位图if (shouldUseWeakCache) {NSString *key = [manager cacheKeyForURL:url context:context];// call memory cache to trigger weak cache sync logic, ignore the return value and go on normal query// this unfortunately will cause twice memory cache query, but it's fast enough// in the future the weak cache feature may be re-design or removed[((SDImageCache *)manager.imageCache) imageFromMemoryCacheForKey:key];}dispatch_main_async_safe(^{//立刻显示占位图[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];});}
-
判断url是否存在,如果存在就开始正式的一个加载流程,重置
NSProgress
、SDWebImageIndicator,
完成下方配置的时候,进入我们的loadImageWithURL
函数,当下载完成的时候根据需要决定有没有过度图片,加载结束后通过Block返回图片就可以了.if (url) {// reset the progressNSProgress *imageProgress = loadState.progress; // 重新处理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//设置一个回调函数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); // 弱引用避免循环引用operation = [manager loadImageWithURL:url options:options context:context progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { //加载图片 (里面采用了强弱共舞的方式保证了block不会发生循环引用)//省略部分代码[self sd_setImageLoadOperation:operation forKey:validOperationKey];//设置image的一个operation//``````}
loadImageWithURL(SDWebManger层)
loadImageWithURL
是SDWebImageManager
中的方法,SDWebImageManager是一个单例,在初始化的时候,还初始化了SDImageCache
和SDWebImageDownloader
。SDWebImageManager的作用就是调度SDImageCache和SDWebImageDownloader进行缓存和下载操作的。
- 参数校验,和前面的方法一样:
if ([url isKindOfClass:NSString.class]) {url = [NSURL URLWithString:(NSString *)url];}// Prevents app crashing on argument type error like sending NSNull instead of NSURLif (![url isKindOfClass:NSURL.class]) {url = nil;}
- 创建操作对象,用来管理下载和缓存的部分:把manger关联上,方便后面的处理
SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];operation.manager = self;
- 判断是否在失败的URL中,防止多次失败下载:
- 这里通过递归锁保证了对共享资源的安全访问,共享资源就是
self.failedURLs
,之所以加锁是为了防止其他线程访问当前集合并对其进行修改
- 这里通过递归锁保证了对共享资源的安全访问,共享资源就是
BOOL isFailedUrl = NO;if (url) {//如果url存在就判断一次SD_LOCK(_failedURLsLock); //加锁避免多线程访问统一块内存isFailedUrl = [self.failedURLs containsObject:url]; SD_UNLOCK(_failedURLsLock);}
- 如果URL无效或是失败的URL没有设置重试选项, 立即调用完成回调并返回错误信息
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {NSString *description = isFailedUrl ? @"Image url is blacklisted" : @"Image url is nil";NSInteger code = isFailedUrl ? SDWebImageErrorBlackListed : SDWebImageErrorInvalidURL;[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : description}] queue:result.context[SDWebImageContextCallbackQueue] url:url];return operation;}
- 如果上述操作都没问题就将当前操作加到操作队列中并且预处理最终结果
// 预处理选项和上下文参数,确定最终的结果。
SD_LOCK(_runningOperationsLock);
//添加到所有的队列中,发现没有什么问题
[self.runningOperations addObject:operation];
SD_UNLOCK(_runningOperationsLock);
最后进入callCacheProcessForOperation
这个函数,这个函数里面会调用queryImageForKey
,通过queryImageForKey
再调用queryCacheOperationForKey
这个函数
queryCacheOperationForKey(SDImageCache层)
这个函数最重要的是查找缓存的内容,
先来看一下有关缓存策略的配置的一个类别,保存缓存策略的信息,比如最长的缓存时间去清除缓存:
@implementation SDImageCacheConfig
- (instancetype)init {if (self = [super init]) {_shouldDisableiCloud = YES;_shouldCacheImagesInMemory = YES;_shouldUseWeakMemoryCache = NO;_shouldRemoveExpiredDataWhenEnterBackground = YES;_shouldRemoveExpiredDataWhenTerminate = YES;_diskCacheReadingOptions = 0;_diskCacheWritingOptions = NSDataWritingAtomic;_maxDiskAge = kDefaultCacheMaxDiskAge;_maxDiskSize = 0;_diskCacheExpireType = SDImageCacheConfigExpireTypeModificationDate;_fileManager = nil;if (@available(iOS 10.0, tvOS 10.0, macOS 10.12, watchOS 3.0, *)) {_ioQueueAttributes = DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL; // DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM} else {_ioQueueAttributes = DISPATCH_QUEUE_SERIAL; // NULL}_memoryCacheClass = [SDMemoryCache class];_diskCacheClass = [SDDiskCache class];}return self;
}
继续看一下SDImageCache
中关于缓存策略的几个枚举:
typedef NS_ENUM(NSInteger, SDImageCacheType) {/*** For query and contains op in response, means the image isn't available in the image cache* For op in request, this type is not available and take no effect.*///图片需要从网络上下载SDImageCacheTypeNone,/*** For query and contains op in response, means the image was obtained from the disk cache.* For op in request, means process only disk cache.*///表示图片来源于磁盘缓存SDImageCacheTypeDisk,/*** For query and contains op in response, means the image was obtained from the memory cache.* For op in request, means process only memory cache.*///表示图片来源内存缓存SDImageCacheTypeMemory,/*** For query and contains op in response, this type is not available and take no effect.* For op in request, means process both memory cache and disk cache.*///表示请求操作,表示同时查询寻内存和磁盘缓存SDImageCacheTypeAll
};
下面就进入我们的SDImageCache
中的初始化操作,SDImageCache
是在Manger
初始化的时候就完成了初始化,现在看一下初始化过程中具体做了什么操作:
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)nsdiskCacheDirectory:(nullable NSString *)directoryconfig:(nullable SDImageCacheConfig *)config {if ((self = [super init])) {NSAssert(ns, @"Cache namespace should not be nil");if (!config) {config = SDImageCacheConfig.defaultCacheConfig;}_config = [config copy];// Create IO queue//创建一个执行IO操作的串行队列,以及NSFileManger的初始化dispatch_queue_attr_t ioQueueAttributes = _config.ioQueueAttributes;_ioQueue = dispatch_queue_create("com.hackemist.SDImageCache.ioQueue", ioQueueAttributes);NSAssert(_ioQueue, @"The IO queue should not be nil. Your configured `ioQueueAttributes` may be wrong");// Init the memory cacheNSAssert([config.memoryCacheClass conformsToProtocol:@protocol(SDMemoryCache)], @"Custom memory cache class must conform to `SDMemoryCache` protocol");_memoryCache = [[config.memoryCacheClass alloc] initWithConfig:_config];//构造SDImageCacheConfig配置对象// Init the disk cacheif (!directory) {// Use default disk cache directorydirectory = [self.class defaultDiskCacheDirectory];}_diskCachePath = [directory stringByAppendingPathComponent:ns];NSAssert([config.diskCacheClass conformsToProtocol:@protocol(SDDiskCache)], @"Custom disk cache class must conform to `SDDiskCache` protocol");//设置内存缓存的name和磁盘缓存的文件路径_diskCache = [[config.diskCacheClass alloc] initWithCachePath:_diskCachePath config:_config];// Check and migrate disk cache directory if need[self migrateDiskCacheDirectory];#if SD_UIKIT// Subscribe to app events//添加应用即将结束和进入后台的通知[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(applicationWillTerminate:)name:UIApplicationWillTerminateNotificationobject:nil];[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(applicationDidEnterBackground:)name:UIApplicationDidEnterBackgroundNotificationobject:nil];
#endif
#if SD_MAC[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(applicationWillTerminate:)name:NSApplicationWillTerminateNotificationobject:nil];
#endif}return self;
}
为缓存做了充足的准备,下面正式存储和查找的流程
- (nullable SDImageCacheToken *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context cacheType:(SDImageCacheType)queryCacheType done:(nullable SDImageCacheQueryCompletionBlock)doneBlock {// 如果key为空,就立刻nil并且调用完成回调if (!key) {if (doneBlock) {doneBlock(nil, nil, SDImageCacheTypeNone);}return nil; //key确保有效且queryCacheType合法.避免无效查询}// Invalid cache type//如果缓存类型位无也立即完成回调if (queryCacheType == SDImageCacheTypeNone) {if (doneBlock) {doneBlock(nil, nil, SDImageCacheTypeNone);}return nil;}// First check the in-memory cache...// 首先检查内存缓存UIImage *image;if (queryCacheType != SDImageCacheTypeDisk) { // 内存缓存查询image = [self imageFromMemoryCacheForKey:key]; //通过NSCache快速查询内存缓存}if (image) { // 如果找到了图象//接吗第一帧保证图片是静态的if (options & SDImageCacheDecodeFirstFrameOnly) {// Ensure static imageif (image.sd_imageFrameCount > 1) {
#if SD_MACimage = [[NSImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:kCGImagePropertyOrientationUp];
#elseimage = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:image.imageOrientation];
#endif}} else if (options & SDImageCacheMatchAnimatedImageClass) {// Check image class matching//根据上下问中的齐王动画图片类型进行一个检查Class animatedImageClass = image.class;Class desiredImageClass = context[SDWebImageContextAnimatedImageClass];if (desiredImageClass && ![animatedImageClass isSubclassOfClass:desiredImageClass]) {image = nil;//不匹配就清空图片}}}BOOL shouldQueryMemoryOnly = (queryCacheType == SDImageCacheTypeMemory) || (image && !(options & SDImageCacheQueryMemoryData)); //内存命中处理 一个是它符合一个内存缓存获取到的图片且不需要查询内存数据,就直接返回结果并完成回调if (shouldQueryMemoryOnly) {if (doneBlock) {doneBlock(image, nil, SDImageCacheTypeMemory);}return nil;}// Second check the disk cache...// 初始化查询操作令牌并且设值相关属性SDCallbackQueue *queue = context[SDWebImageContextCallbackQueue];SDImageCacheToken *operation = [[SDImageCacheToken alloc] initWithDoneBlock:doneBlock];operation.key = key;operation.callbackQueue = queue;// Check whether we need to synchronously query disk// 1. in-memory cache hit & memoryDataSync// 2. in-memory cache miss & diskDataSync//判断内存缓存命中需要同步查询磁盘缓存//内存缓存未命中且要求同步查询磁盘数据BOOL shouldQueryDiskSync = ((image && options & SDImageCacheQueryMemoryDataSync) ||(!image && options & SDImageCacheQueryDiskDataSync));//定义从磁盘查询数据BlockNSData* (^queryDiskDataBlock)(void) = ^NSData* { // 定义Block,对取消操作就进行操作@synchronized (operation) {if (operation.isCancelled) {return nil;}}// 如果操作没有被取消,从磁盘的所有可能路径中搜索数据return [self diskImageDataBySearchingAllPathsForKey:key];};// 定义从磁盘加载创建的图象的Block,根据磁盘数据生成一个image对象UIImage* (^queryDiskImageBlock)(NSData*) = ^UIImage*(NSData* diskData) {@synchronized (operation) {if (operation.isCancelled) {return nil;}}UIImage *diskImage;if (image) {// the image is from in-memory cache, but need image data//图片依从内存缓存获取,仅需根据数据生成UIImagediskImage = image;} else if (diskData) { //从磁盘获取数据,需要解析位UIImageBOOL shouldCacheToMemory = YES;if (context[SDWebImageContextStoreCacheType]) {SDImageCacheType cacheType = [context[SDWebImageContextStoreCacheType] integerValue];shouldCacheToMemory = (cacheType == SDImageCacheTypeAll || cacheType == SDImageCacheTypeMemory);}// Special case: If user query image in list for the same URL, to avoid decode and write **same** image object into disk cache multiple times, we query and check memory cache here again.if (shouldCacheToMemory && self.config.shouldCacheImagesInMemory) {diskImage = [self.memoryCache objectForKey:key];}// decode image data only if in-memory cache missed//如果内存缓存未命中,解码磁盘数据if (!diskImage) {diskImage = [self diskImageForKey:key data:diskData options:options context:context];// check if we need sync logicif (shouldCacheToMemory) {[self _syncDiskToMemoryWithImage:diskImage forKey:key];}}}return diskImage;};// Query in ioQueue to keep IO-safeif (shouldQueryDiskSync) {__block NSData* diskData;__block UIImage* diskImage;dispatch_sync(self.ioQueue, ^{diskData = queryDiskDataBlock();diskImage = queryDiskImageBlock(diskData);});if (doneBlock) {doneBlock(diskImage, diskData, SDImageCacheTypeDisk);}} else {dispatch_async(self.ioQueue, ^{NSData* diskData = queryDiskDataBlock();UIImage* diskImage = queryDiskImageBlock(diskData);@synchronized (operation) {if (operation.isCancelled) {return;}}if (doneBlock) {[(queue ?: SDCallbackQueue.mainQueue) async:^{// Dispatch from IO queue to main queue need time, user may call cancel during the dispatch timing// This check is here to avoid double callback (one is from `SDImageCacheToken` in sync)@synchronized (operation) {if (operation.isCancelled) {return;}}doneBlock(diskImage, diskData, SDImageCacheTypeDisk);}];}});}return operation;
}
上面代码主要就做了这几件事情:
- 先检查键值是否为空并且图片leix是否哦合法,如果不为空并且合法的情况在执行下米纳的操作,否则就直接执行回调
- 在内粗那种查找缓存,如果找到了就图象就先处理第一帧来保证图片是静态的
- 检查是否只需要查询内存,如果质用查询内存的话就立即回调
- 下面选择异步还是同步查询磁盘缓存
- 磁盘中且内存中找到缓存的话就开始解析图片.如果内存中没有缓存就会先从磁盘传向内存中,如果磁盘中也没有找到缓存的画家就开始执行下载任务,如果要执行下载在操作的话就是调用
SDWebManger
层的callDownloadProcessForOperation
删除缓存
-
需要一个文件的迭代器fileEnumerator去获取需要删除的图片文件
-
根据前面设置的清除缓存的策略中最大时间限制(默认一周),计算出文件(创建或上一次修改)需要被清除的时间范围
-
根据这个时间范围遍历出需要删除的图片,然后从磁盘中删除
-
再根据缓存策略中配置的最大缓存大小,判断时候需要进一步清除缓存
-
按创建的先后顺序继续删除缓存,直到缓存大小是最大值的一半
callDownloadProcessForOperation(SDWebImagerManger层)
这个方法的作用就是在下载图片
- (void)callDownloadProcessForOperation:(nonnull SDWebImageCombinedOperation *)operationurl:(nonnull NSURL *)urloptions:(SDWebImageOptions)optionscontext:(SDWebImageContext *)contextcachedImage:(nullable UIImage *)cachedImagecachedData:(nullable NSData *)cachedDatacacheType:(SDImageCacheType)cacheTypeprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock {// Mark the cache operation end//标记缓存操作的结束@synchronized (operation) {operation.cacheOperation = nil; //情空当前曹组哟的缓存操作引用}// Grab the image loader to use// 获取图片加载器,优先使用上下文提供的,否则使用默认的imageLoader属性id<SDImageLoader> imageLoader = context[SDWebImageContextImageLoader];if (!imageLoader) {imageLoader = self.imageLoader;}// Check whether we should download image from network//判断是否需要从网络加载图片BOOL shouldDownload = !SD_OPTIONS_CONTAINS(options, SDWebImageFromCacheOnly);shouldDownload &= (!cachedImage || options & SDWebImageRefreshCached);shouldDownload &= (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);if ([imageLoader respondsToSelector:@selector(canRequestImageForURL:options:context:)]) {shouldDownload &= [imageLoader canRequestImageForURL:url options:options context:context];} else {shouldDownload &= [imageLoader canRequestImageForURL:url];}if (shouldDownload) { // 需要下载图片的情况if (cachedImage && options & SDWebImageRefreshCached) {// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.//通知以找到的缓存图片并且尝试冲洗下载来更新缓存[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES queue:context[SDWebImageContextCallbackQueue] url:url];// 将缓存图片传递给图片加载器,以便比较远程图片是否与缓存图片一致// Pass the cached image to the image loader. The image loader should check whether the remote image is equal to the cached image.SDWebImageMutableContext *mutableContext;if (context) {mutableContext = [context mutableCopy];} else {mutableContext = [NSMutableDictionary dictionary];}mutableContext[SDWebImageContextLoaderCachedImage] = cachedImage;context = [mutableContext copy];}@weakify(operation);//处理循环引用operation.loaderOperation = [imageLoader requestImageWithURL:url options:options context:context progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {@strongify(operation);if (!operation || operation.isCancelled) { // 操作被用户取消// Image combined operation cancelled by user[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during sending the request"}] queue:context[SDWebImageContextCallbackQueue] url:url];} else if (cachedImage && options & SDWebImageRefreshCached && [error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorCacheNotModified) {// Image refresh hit the NSURLCache cache, do not call the completion block} else if ([error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorCancelled) {// Download operation cancelled by user before sending the request, don't block failed URL[self callCompletionBlockForOperation:operation completion:completedBlock error:error queue:context[SDWebImageContextCallbackQueue] url:url];} else if (error) {[self callCompletionBlockForOperation:operation completion:completedBlock error:error queue:context[SDWebImageContextCallbackQueue] url:url];BOOL shouldBlockFailedURL = [self shouldBlockFailedURLWithURL:url error:error options:options context:context];if (shouldBlockFailedURL) {SD_LOCK(self->_failedURLsLock);[self.failedURLs addObject:url];SD_UNLOCK(self->_failedURLsLock);}} else {if ((options & SDWebImageRetryFailed)) {SD_LOCK(self->_failedURLsLock);[self.failedURLs removeObject:url];SD_UNLOCK(self->_failedURLsLock);}// Continue transform process[self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData cacheType:SDImageCacheTypeNone finished:finished completed:completedBlock];}if (finished) {[self safelyRemoveOperationFromRunning:operation];}}];} else if (cachedImage) { // 仅使用缓存图片的情况[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES queue:context[SDWebImageContextCallbackQueue] url:url];[self safelyRemoveOperationFromRunning:operation];} else { // 未找到缓存图片且不允许下载的情况// Image not in cache and download disallowed by delegate[self callCompletionBlockForOperation:operation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES queue:context[SDWebImageContextCallbackQueue] url:url];[self safelyRemoveOperationFromRunning:operation];}
}
-
判断是否需要从网络下载图片;
-
若要下载,则发起下载请求;
-
处理下载结果(成功、失败、取消);
-
回调对应的
completedBlock
; -
清理状态。
这里通过这个函数来正式开始从网络上加载图片:
requestImageWithURL
这个函数会调用downloadImageWithURL
这才算是我们下载的核心函数:
downloadImageWithURL(SDImageLoadersManager层)
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageDownloaderOptions)optionscontext:(nullable SDWebImageContext *)contextprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {// 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.//处理urlif (url == nil) {if (completedBlock) {NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey : @"Image url is nil"}];completedBlock(nil, nil, error, YES);}return nil;}//初始化下载操作取消令牌,用于取消关联的下载操作id downloadOperationCancelToken;// When different thumbnail size download with same url, we need to make sure each callback called with desired size//根据上下文德缓存key生成缓存建用于唯一标识图片资源id<SDWebImageCacheKeyFilter> cacheKeyFilter = context[SDWebImageContextCacheKeyFilter];NSString *cacheKey;if (cacheKeyFilter) {cacheKey = [cacheKeyFilter cacheKeyForURL:url];} else {cacheKey = url.absoluteString;}//根据上下文和下载选项生成解码选项SDImageCoderOptions *decodeOptions = SDGetDecodeOptionsFromContext(context, [self.class imageOptionsFromDownloaderOptions:options], cacheKey); //加锁SD_LOCK(_operationsLock); //下载操作的复用和创建NSOperation<SDWebImageDownloaderOperation> *operation = [self.URLOperations objectForKey:url];// There is a case that the operation may be marked as finished or cancelled, but not been removed from `self.URLOperations`.//检查是否可以复用现有的下载操作BOOL shouldNotReuseOperation;if (operation) {@synchronized (operation) {shouldNotReuseOperation = operation.isFinished || operation.isCancelled;}} else {shouldNotReuseOperation = YES;}if (shouldNotReuseOperation) {//创建新的下载操作 operation = [self createDownloaderOperationWithUrl:url options:options context:context];if (!operation) {SD_UNLOCK(_operationsLock);if (completedBlock) {NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidDownloadOperation userInfo:@{NSLocalizedDescriptionKey : @"Downloader operation is nil"}];completedBlock(nil, nil, error, YES);}return nil;}//设置操作完成的时候从操作字典移除这个操作@weakify(self);operation.completionBlock = ^{@strongify(self);if (!self) {return;}SD_LOCK(self->_operationsLock);[self.URLOperations removeObjectForKey:url];SD_UNLOCK(self->_operationsLock);};[self.URLOperations setObject:operation forKey:url]; //设置字典对应的key和value// Add the handlers before submitting to operation queue, avoid the race condition that operation finished before setting handlers.downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock decodeOptions:decodeOptions];// Add operation to operation queue only after all configuration done according to Apple's doc.// `addOperation:` does not synchronously execute the `operation.completionBlock` so this will not cause deadlock.[self.downloadQueue addOperation:operation];} else {// When we reuse the download operation to attach more callbacks, there may be thread safe issue because the getter of callbacks may in another queue (decoding queue or delegate queue)// So we lock the operation here, and in `SDWebImageDownloaderOperation`, we use `@synchonzied (self)`, to ensure the thread safe between these two classes.@synchronized (operation) {downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock decodeOptions:decodeOptions];}}SD_UNLOCK(_operationsLock);SDWebImageDownloadToken *token = [[SDWebImageDownloadToken alloc] initWithDownloadOperation:operation];token.url = url;token.request = operation.request;token.downloadOperationCancelToken = downloadOperationCancelToken;return token;
}
这个方法主要是为了图片下载任务提供一个统一的入口,管理下载操作的生命周期,回调处理和线程安全.这里的createDownloaderOperationWithUrl
这个函数是真正触发下载的流程.在执行完成后返回一个operation通过operation进行后面的操作最后返回一个token。
这个返回类型这么做的一个目的就是可以在取消的回调中及时取消下载操作.
storeImage(SDImageCache)
完成下载后,会回到Mnager层级然后在调用Cache层级的storeImage函数把图片保存到我们的cache中.,先会保存道内存中,如果还需要保存到磁盘中那就再深入一级保存到磁盘中
setImage(SDWebImageManager)
这里我们返回到这里通过这个单例类来将image设置到对应的一个视图上.
相关文章:
【iOS】SDWebImage源码学习
SDWebImage源码学习 文章目录 SDWebImage源码学习前言SDWebImage缓存流程sd_setImageWithURL(UIImageViewWebCache层)sd_internalSetImageWithURL(UIViewWebCache层)loadImageWithURL(SDWebManger层)queryCacheOperationForKey(SDImageCache层)删除缓存 callDownloadProcessFor…...
.Net HttpClient 处理响应数据
HttpClient 处理响应数据 1、初始化及全局设置 //初始化:必须先执行一次 #!import ./ini.ipynb2、处理响应状态 //判断响应码:正常 {var response await SharedClient.GetAsync("api/Normal/GetAccount?id1");if(response.StatusCode Sy…...
【心海资源】【最新话费盗u】【未测】提币对方官方波场+没有任何加密+无后门+前端VUE
提笔接口请使用官方提笔,第三方提笔都有风险 后门你们也扫扫,这种源码风险大,自己玩玩学习进行了 重要的事情说三遍 !!!!!!!!!&…...
Python中的标识、相等性与别名:深入理解对象引用机制
在Python编程中,理解变量如何引用对象以及对象之间的比较方式是至关重要的基础概念。本文将通过Lewis Carroll的笔名示例,深入探讨Python中的对象标识、相等性判断以及别名机制。 别名现象:变量共享同一对象 >>> charles {name: …...
Java 1.8(也称为Java 8)
Java 1.8(也称为Java 8)是Oracle于2014年发布的一个重要版本,引入了许多新特性和改进,极大地提升了Java语言的表达力和开发效率。以下是Java 1.8的主要新特性: ### 1. Lambda表达式 Lambda表达式是Java 1.8最具革命性…...
LVGL简易计算器实战
文章目录 📁 文件结构建议🔹 eval.h 表达式求值头文件🔹 eval.c 表达式求值实现文件(带详细注释)🔹 ui.h 界面头文件🔹 ui.c 界面实现文件🔹 main.c 主函数入口✅ 总结 项目效果&…...
Linux | Uboot-Logo 修改文档(第十七天)
01 Uboot 修改 首先我们在 home 目录下新建一个文件夹 imx6ull,然后打开 i.MX6ULL 终结者光盘资料\05_uboot linux源码,在 window 下解压下图箭头所指的压缩包,解压后分别得到 linux-imx-rel_imx_4.1.15_2.1.0_ga_20200323.tar.gz 和 uboot-imx-rel_imx_4.1.15_2.1.0_…...
数字孪生概念
数字孪生(Digital Twin) 是指通过数字技术对物理实体(如设备、系统、流程或环境)进行高保真建模和实时动态映射,实现虚实交互、仿真预测和优化决策的技术体系。它是工业4.0、智慧城市和数字化转型的核心技术之一。 1. …...
c++STL-string的使用
这里写自定义目录标题 string的使用string写成类模板的原因string的版本举例构造、析构函数和赋值重载构造函数和析构函数operator Iterators迭代器begin和endrbegin和rendcbegin和cend,crbegin和crend(c11) capacity容量有关函数不同编译器下…...
总结C/C++中程序内存区域划分
C/C程序内存分配的⼏个区域 1..栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时 这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很⾼,…...
C# 方法(方法重载)
本章内容: 方法的结构 方法体内部的代码执行 局部变量 局部常量 控制流 方法调用 返回值 返回语句和void方法 局部函数 参数 值参数 引用参数 引用类型作为值参数和引用参数 输出参数 参数数组 参数类型总结 方法重载 命名参数 可选参数 栈帧 递归 方法重载 一个类中可以有多个…...
Dockerfile 完全指南:从入门到最佳实践
Dockerfile 完全指南:从入门到最佳实践 1. Dockerfile 简介与作用 Dockerfile 是一个文本文件,包含了一系列用于构建 Docker 镜像的指令。它允许开发者通过简单的指令定义镜像的构建过程,实现自动化、可重复的镜像构建。 主要作用…...
DEEPPOLAR:通过深度学习发明非线性大核极坐标码(2)
目录 2.问题的提出和背景 2.1 信道编码 2.2.极化码 极坐标编码 极坐标解码 原文:《DEEPPOLAR: Inventing Nonlinear Large-Kernel Polar Codes via Deep Learning》 2.问题的提出和背景 2.1 信道编码 信道编码是一种为传输添加冗余的技术,使其对…...
ESP32-S3 学习笔记(1)
ESP32-S3 学习笔记(1) 背景环境添加工程文件材料准备轻触开关的正负极 背景 闲来无事,看到立创论坛上有许多大佬开源的项目,甚是厉害,于是决定自己也来搞一搞,同时可以做一些技术积累,看了很…...
Python Cookbook-7.9 访问 MySQL 数据库
任务 想访问一个 MySQL 数据库。 解决方案 MySQLdb 模块正是为这种任务而设计的: import MySQLdb #创建一个连接对象,再用它创建游标 con = MySQLdb.connect(host = "127.0.0.1", port = 3306, user = "joe",<...
docker安装superset实践
1、拉取docker镜像 docker pull apache/superset:latest 2、安装superset容器 mkdir /usr/local/develop/docker/superset/ -p touch /usr/local/develop/docker/superset/superset_config.py superset_config.py配置文件如下: SQLALCHEMY_DATABASE_URI mysql:…...
Web开发—Vue工程化
文章目录 前言 Vue工程化 一、介绍 二、环境准备 1.介绍create-vue 2.NodeJS安装 3.npm介绍 三,Vue项目创建 四,项目结构 五,启动项目 六,Vue项目开发流程 七,API风格 前言 Vue工程化 前面我们在介绍Vue的时候&#…...
什么是硬件中断请求号?什么是中断向量号?
一、硬件中断请求号(IRQ,Interrupt Request Number) 定义: 硬件中断请求号(IRQ)是硬件设备向CPU发送中断请求时使用的唯一标识符,用于区分不同的中断源。例如,键盘、硬盘等外设…...
[Java实战]Spring Boot 定时任务(十五)
[Java实战]Spring Boot 定时任务(十五) 一、定时任务的应用场景 数据同步:每日凌晨同步第三方数据状态检查:每5分钟扫描订单超时未支付资源清理:每小时清理临时文件报表生成:每月1号生成财务统计报表通知…...
OpenWrt开发第7篇:OpenWrt配置支持Web界面
文/指尖动听知识库-谷谷 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:Openwrt开发-基于Raspberry Pi 4B开发板 OpenWrt的luci是一个基于Web的图形化管理界面,为用户提供了直观的操作方式,无需命令行即可完成大部分功能的配置。 1.在终端输入ma…...
【多模态】IMAGEBIND论文阅读
every blog every motto: Although the world is full of suffering, it is full also of the overcoming of it 0. 前言 IMAGEBIND 多模态论文梗概 IMAGEBIND是一种夸模态的神经网络,以图片为中心,联合六中模态的网络(图片、文…...
【C语言干货】二维数组传参本质
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、二维数组的内存布局 1.二维数组的实质2.二维数组的地址关系 二、二维数组传参的本质 1.参数传递的退化机制2.三种等效的函数声明方式 总结 前言 提示&#…...
基于SpringBoot的抽奖系统测试报告
一、编写目的 本报告为抽奖系统测试报告,本项目可用于团体抽奖活动,包括了用户注册,用户登录,修改奖项以及抽奖等功能。 二、项目背景 抽奖系统采用前后端分离的方法来实现,同时使用了数据库来存储相关的数据&…...
Go语言从零构建SQL数据库(9)-数据库优化器的双剑客
数据库优化器的双剑客:谓词下推与列裁剪 在数据库查询优化的世界里,有两位特别重要的"超级英雄":谓词下推和列裁剪。这两种优化技术虽然简单,却能带来惊人的性能提升。今天,我们就来揭开它们的神秘面纱&…...
C++中什么是函数指针?
在C中,函数指针是一个指向函数的指针变量。通过函数指针,我们可以像使用函数一样调用它所指向的函数,常用于实现回调函数、函数指针数组等功能。 以下是一个简单的C代码示例,展示了函数指针的使用: cpp #include <…...
Python工具链UV整合环境管理
Python工具链UV整合环境管理 终极Python工具链UV:从依赖管理到项目开发的全维度解析一、引言:重新定义Python开发的大一统时代二、深度安装指南:多场景适配方案1. 官方独立安装器(推荐方案)2. 进阶安装方式3. 安装验证…...
RuoYi-v4.7.8 jar/war部署
准备条件 jdk-8u73-windows-x64.exe mysql5.7 apache-tomcat-9.0.60 apache-maven-3.8.1 RuoYi-v4.7.8.zip (官网 RuoYi) 登录gitee,选择标签要下载的版本好,点击克隆下载zip压缩文件 安装maven Apache Archive Distribution Directory…...
基于SpringBoot的小区停车位管理系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...
张量并行优质博客
必读图解系列1 比较全面的相关文献总结博客,可以重点看一下其中的行列切分算子2 # 图解大模型训练之:张量模型并行(TP),Megatron-LM ↩︎ # 大规模分布式 AI 模型训练系列——张量并行 ↩︎...
汽车诊断简介
历史 20世纪80年代,由于美国西海岸严重的雾霾问题,CARB(加州空气资源委员会)通过了一项法律,要求对机动车辆进行车载监测诊断。这推动了OBD-I的引入,并在1990年代被OBD II取代。与此同时,欧洲也…...
suricata之规则去重
一、环境和背景 1.1 环境 OS: Ubuntu 22.04.5 LTS IDE: vscode suricata: suricata 7.0.5 1.2 背景 在添加规则时,为了给规则分类,将不同类别的规则写入不同的文件。 在规则加载时两条不同的规则却被认为是重复的,因此记录一下去重逻辑。…...
接口在函数参数和对象类型中的应用
在 TypeScript 中,接口(interface)是一种强大的工具,用于定义和约束对象的结构。它不仅可以用于描述对象类型,还能够用于定义函数的参数和返回类型。接口可以提高代码的可读性、可维护性,并帮助捕捉潜在的错…...
Javascript:数组和函数
数组 创建数组 使用new创建 let arrnew array(数组大小); 直接赋值创建 let Arr2[];let Arr3[1,A,"HELLLO"]; 这里JS的数组里面的元素属性可以各不相同 演示代码 <script>let Arr1new Array(5);let Arr2[];let Arr3[1,A,"HELLLO"];console.…...
Vue Router
Vue Router:前端路由跳转的魔法 什么是Vue Router Vue Router是Vue官方的路由管理器,它允许我们在不重新加载页面的情况下更改浏览器中显示的内容,实现单页应用(SPA)的无缝导航体验。 📊 Vue Router工作流程图 #mermaid-svg-xNtkA0qYMjB0lvUt {font-family:"trebu…...
谷歌与微软的AI战争:搜索、云服务与生态布局
谷歌与微软的AI战争:搜索、云服务与生态布局 系统化学习人工智能网站(收藏):https://www.captainbed.cn/flu 文章目录 谷歌与微软的AI战争:搜索、云服务与生态布局摘要引言技术路线对比1. AI基础设施:算力…...
robomaster机甲大师--电调电机
文章目录 C620电调ID设置速率 电调发送报文电调接收报文cubemx程序初始化发送接收 C620电调 ID设置 速率 1Mbps 电调发送报文 发送的数据为控制电机的输出电流,需要将can数据帧的ID设置为0x200 电调接收报文 机械角度:电机的0到360度映射到0到几千转…...
菜鸟之路day31一一MySQL之多表设计
菜鸟之路day31一一MySQL之多表设计 作者:blue 时间:2025.5.9 文章目录 菜鸟之路day31一一MySQL之多表设计0.概述一.多表设计1.1一对多1.2一对一1.3多对多 0.概述 内容学习自黑马程序员BV1m84y1w7Tb 一.多表设计 1.1一对多 一对多关系实现ÿ…...
害怕和别人发生冲突怎么办? --deepseek
害怕与他人发生冲突是一种常见的心理状态,可能源于对关系破裂、被否定或情绪失控的担忧。但这种恐惧长期存在会影响自我表达和人际关系。以下是分步骤的应对策略,帮助你逐步建立应对冲突的自信: 第一步:理解你的恐惧根源 自我提问…...
Go语言——kratos微服务框架使用
文章目录 一、安装依赖二、创建项目三、初始化项目四、使用git_bash命令终端运行命令五、创建自己的项目1、修改app.proto3、internal/service/app.go4、修改internal/service/service.go文件5、创建internal/biz/content.go文件6、修改internal/biz/biz.go文件7、创建internal…...
无人机飞控算法开发实战:从零到一构建企业级飞控系统
简介 无人机飞控算法是实现稳定飞行和精确控制的核心技术,涉及飞行动力学建模、传感器数据处理、状态估计和控制策略等多个环节。本实战指南将系统讲解四旋翼无人机飞控算法的开发流程,包括飞行动力学模型建立、传感器校准与数据融合、主流控制算法实现(PID、ADRC、EKF)以…...
MiniMind:3块钱成本 + 2小时!训练自己的0.02B的大模型。minimind源码解读、MOE架构
大家好,我是此林。 目录 1. 前言 2. minimind模型源码解读 1. MiniMind Config部分 1.1. 基础参数 1.2. MOE配置 2. MiniMind Model 部分 2.1. MiniMindForCausalLM: 用于语言建模任务 2.2. 主干模型 MiniMindModel 2.3. MiniMindBlock: 模型的基本构建块…...
每日算法刷题 Day3 5.11:leetcode数组2道题,用时1h(有点慢)
5.LC 零矩阵(中等) 面试题 01.08. 零矩阵 - 力扣(LeetCode) 思想: 法一: 利用两个集合分别储存要清0的行和列索引 另外两种原地优化空间的做法暂时不是目前刷题目标,故不考虑 代码 c: class Solution { public:void setZeroes(vector&l…...
POSIX信号量
目录 一、相关概念回顾 1.信号量 2.多线程使用资源的两种情况 3.P操作和V操作 二、CP && 基于环形队列的生产者消费者模型 1.环形队列的介绍 编辑 2.基于环形队列的生产者消费者模型的默认规则(通过信号量实现规则的成立) 3.相关的结论…...
前端Web开发HTML5+CSS3+移动web(基础-flex)
网页设计套路:从上到下,从整体到局部 1:HTML定义: (1)超文本是点击可以页面来回切换的链接 (2)标记就是标签语言 2:标签的语法 (1&…...
Java 原生异步编程与Spring 异步编程 详解
简介 Java 异步编程是现代高性能应用开发的核心技术之一,它允许程序在执行耗时操作(如网络请求、文件 IO)时不必阻塞主线程,从而提高系统吞吐量和响应性。 异步 vs 同步 同步:任务按顺序执行,后续任务需…...
AUTOSAR图解==>AUTOSAR_TR_HWTestManagementIntegrationGuide
AUTOSAR硬件测试管理集成指南 启动和关闭阶段硬件测试管理的规范与集成 目录 文档概述 1.1 文档范围 1.2 局限性目标与动机 2.1 目标 2.2 动机 2.3 用例约束与假设缩略语与术语相关文档HTMSS AUTOSAR集成方法HTMSS功能描述AUTOSAR架构解决方案 8.1 HTMSS系统架构 8.2 HTMSS启动…...
Day22 Kaggle泰坦尼克号训练实战
作业 自行学习参考如何使用kaggle平台,写下使用注意点,并对下述比赛提交代码 kaggle泰坦里克号人员生还预测 一、流程 思路概述 数据加载 :读取泰坦尼克号的训练集和测试集。数据预处理 :处理缺失值、对分类变量进行编码、…...
基于大核感知与非膨胀卷积的SPPF改进—融合UniRepLK的YOLOv8目标检测创新架构
在当前目标检测领域中,YOLO系列模型因其优异的速度-精度平衡能力而被广泛部署于工业界与科研场景。YOLOv8作为该系列的最新版本,在主干网络与特征金字塔结构上进行了多项优化,进一步提升了其实时性与鲁棒性。然而,其核心组件—SPP…...
[Linux]从零开始的STM32MP157 Busybox根文件系统构建
一、前言 在上一篇教程中,已经教了大家如何使用Buildroot构建根文件系统,并且在最后我们已经完整的构建了一个可以运行的根文件系统。但是,Buildroot的集成度太高了,不利于小白理解根文件系统,所以本次教程,…...
C++ RAII机制
RAII(Resource Acquisition Is Initialization)是一种编程范式,核心思想是:资源的生命周期与对象绑定——对象创建时获取资源,对象销毁时自动释放资源。这种机制通过构造函数和析构函数的配对执行,确保资源…...