版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.02.23 |
前言
我们做APP,文字和图片是绝对不可缺少的元素,特别是图片一般存储在图床里面,一般公司可以委托第三方保存,NB的公司也可以自己存储图片,ios有很多图片加载的第三方框架,其中最优秀的莫过于SDWebImage,它几乎可以满足你所有的需求,用了好几年这个框架,今天想总结一下。感兴趣的可以看其他几篇。
1. SDWebImage探究(一)
2. SDWebImage探究(二)
3. SDWebImage探究(三)
4. SDWebImage探究(四)
5. SDWebImage探究(五)
6. SDWebImage探究(六) —— 图片类型判断深入研究
7. SDWebImage探究(七) —— 深入研究图片下载流程(一)之有关option的位移枚举的说明
8. SDWebImage探究(八) —— 深入研究图片下载流程(二)之开始下载并返回下载结果的总的方法
9. SDWebImage探究(九) —— 深入研究图片下载流程(三)之下载之前的缓存查询操作
10. SDWebImage探究(十) —— 深入研究图片下载流程(四)之查询缓存后的block回调处理
回顾
上一篇文章我们讲述了查询缓存后的block回调处理,其中就有SDWebImageDownloadToken
这个token的获取,这个token主要作用就是关联每一个下载操作,可以用于取消一个下载,这一篇就详细的说明一下这个token是如何生成和返回的。
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
这个方法就是返回token的方法,Creates a SDWebImageDownloader async downloader instance with a given URL
,也可以这里理解,根据给定的url创建一个SDWebImageDownloader
异步的下载器。
SDWebImageDownloadToken的生成及返回
我们先看一下上面返回token方法的实现。
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(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
NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
cachePolicy:cachePolicy
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;
if (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];
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;
}];
}
这里调用方法addProgressCallback
,返回这个SDWebImageDownloadToken
。
1. SDWebImageDownloadToken的生成过程
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
forURL:(nullable NSURL *)url
createCallback:(SDWebImageDownloaderOperation *(^)(void))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.
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return nil;
}
__block SDWebImageDownloadToken *token = nil;
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
if (!operation) {
operation = createCallback();
self.URLOperations[url] = operation;
__weak SDWebImageDownloaderOperation *woperation = operation;
operation.completionBlock = ^{
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
});
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
token = [SDWebImageDownloadToken new];
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
});
return token;
}
下面我们就一下来看一下这个方法的具体实现。
(a) 容错处理
首先进行的还是容错处理,判断url是否为空,如果为空,就调用completedBlock(nil, nil, nil, NO);
,返回的图像的数据都为空,并return nil
。
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return nil;
}
(b) 设置阻塞队列并在其中生成token
这里使用的是dispatch_barrier_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
,同步阻塞队列,与其类似的还有一个是异步阻塞队列,他们的区别和使用,后面我会和大家分篇进行说明。
这里定义一个用于存放操作的字典,key就是url
@property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations;
先查看该操作是否存在字典中
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
然后调用NSOperation中的completionBlock
,@property (nullable, copy) void (^completionBlock)(void) API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
operation.completionBlock = ^{
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
});
};
在其中做的操作就是判断该操作是不是在字典中,如果在,就从字典中移除。这里仍然用的是阻塞dispatch_barrier_sync
的block。
然后调用方法,生成token中的一个属性token.downloadOperationCancelToken
,并赋值。
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
dispatch_barrier_async(self.barrierQueue, ^{
[self.callbackBlocks addObject:callbacks];
});
return callbacks;
}
这里定义了一个字典,将相关的block copy后进行存储在字典中,并且将该字典存在一个可变的数组callbackBlocks
中。
typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
static NSString *const kProgressCallbackKey = @"progress";
static NSString *const kCompletedCallbackKey = @"completed";
@property (strong, nonatomic, nonnull) NSMutableArray<SDCallbacksDictionary *> *callbackBlocks;
SDWebImageDownloaderOperation的生成
生成token的那个方法中有一个block createCallback:(SDWebImageDownloaderOperation *(^)(void))createCallback
,它是一个具有返回值的block,大家还记得这句代码吗?operation = createCallback();
,当字典中查找的这个operation为nil的时候,就执行这个block返回一个SDWebImageDownloaderOperation
对象。
下面我们就一起看一下这个SDWebImageDownloaderOperation
的生成过程。
(a)定义下载的超时
这里定义的是下载的超时,是15s,并且进行了处理,如果时间间隔为0,那么就重新赋值为15s。
NSTimeInterval timeoutInterval = sself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
(b)缓存策略
为了防止潜在的重复缓存(NSURLCache + SDImageCache)
,我们禁用图像请求的缓存。
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
cachePolicy:cachePolicy
timeoutInterval:timeoutInterval];
这里使用一个三目运算符,当传入的options是SDWebImageDownloaderUseNSURLCache
时,那么NSURLRequestCachePolicy
的实例化对象cachePolicy
就是NSURLRequestUseProtocolCachePolicy
否则就是NSURLRequestReloadIgnoringLocalCacheData
。
下面我们看一下这个NSURLRequestCachePolicy
枚举值
/*!
@enum NSURLRequestCachePolicy
@discussion The NSURLRequestCachePolicy enum defines constants that
can be used to specify the type of interactions that take place with
the caching system when the URL loading system processes a request.
Specifically, these constants cover interactions that have to do
with whether already-existing cache data is returned to satisfy a
URL load request.
NSURLRequestCachePolicy枚举定义了常量可以用来当指定URL加载系统处理请求时的
缓存系统发生时的交互类型 。具体来说,这些常量涵盖了必须做的交互,用来处理是否返回
已经存在的缓存数据以满足URL加载请求。
@constant NSURLRequestUseProtocolCachePolicy Specifies that the
caching logic defined in the protocol implementation, if any, is
used for a particular URL load request. This is the default policy
for URL load requests.
NSURLRequestUseProtocolCachePolicy指定协议实现中定义的缓存逻辑(如果有)
用于特定的URL加载请求。 这是URL加载请求的默认策略。
@constant NSURLRequestReloadIgnoringLocalCacheData Specifies that the
data for the URL load should be loaded from the origin source. No
existing local cache data, regardless of its freshness or validity,
should be used to satisfy a URL load request.
NSURLRequestReloadIgnoringLocalCacheData指定应该从原始源加载URL加载的数据。
不使用现有的本地缓存数据(不管其新鲜度或有效性)满足URL加载请求。
@constant NSURLRequestReloadIgnoringLocalAndRemoteCacheData Specifies that
not only should the local cache data be ignored, but that proxies and
other intermediates should be instructed to disregard their caches
so far as the protocol allows. Unimplemented.
NSURLRequestReloadIgnoringLocalAndRemoteCacheData指定不仅忽略本地缓存数据,而且
应该指示代理和其他中转忽略它们的缓存,只要协议允许。未实现。
@constant NSURLRequestReloadIgnoringCacheData Older name for
NSURLRequestReloadIgnoringLocalCacheData.
NSURLRequestReloadIgnoringLocalCacheData的较早名称。
@constant NSURLRequestReturnCacheDataElseLoad Specifies that the
existing cache data should be used to satisfy a URL load request,
regardless of its age or expiration date. However, if there is no
existing data in the cache corresponding to a URL load request,
the URL is loaded from the origin source.
NSURLRequestReturnCacheDataElseLoad指定应使用现有缓存数据来满足URL加载请求,
而不管其缓存时间或过期日期。 但是,如果没有与URL加载请求相对应的现有的缓存数据,
则URL将从源数据源加载。
@constant NSURLRequestReturnCacheDataDontLoad Specifies that the
existing cache data should be used to satisfy a URL load request,
regardless of its age or expiration date. However, if there is no
existing data in the cache corresponding to a URL load request, no
attempt is made to load the URL from the origin source, and the
load is considered to have failed. This constant specifies a
behavior that is similar to an "offline" mode.
NSURLRequestReturnCacheDataDontLoad指定应使用现有缓存数据来满足URL加载请求,
而不管其缓存时间或到期日期。 但是,如果缓存中没有与URL加载请求相对应的现有数据,
则不会尝试从源数据源加载URL,并且认为加载失败。 此常数指定与“离线”模式类似的行为。
@constant NSURLRequestReloadRevalidatingCacheData Specifies that
the existing cache data may be used provided the origin source
confirms its validity, otherwise the URL is loaded from the
origin source. Unimplemented.
NSURLRequestReloadRevalidatingCacheData指定可以使用现有的缓存数据,只要原始源
确认其有效性,否则URL将从原始源加载。未实现。
*/
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
NSURLRequestUseProtocolCachePolicy = 0,
NSURLRequestReloadIgnoringLocalCacheData = 1,
NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
NSURLRequestReturnCacheDataElseLoad = 2,
NSURLRequestReturnCacheDataDontLoad = 3,
NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented
};
这里给大家翻译了一下,看了应该好理解了不少吧。
(c)cookies处理
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
只要options是SDWebImageDownloaderHandleCookies,那么就是YES,否则就是NO。
这个HTTPShouldHandleCookies
是NSURLRequest的属性。
/*!
@abstract Decide whether default cookie handling will happen for
this request (YES if cookies should be sent with and set for this request;
otherwise NO).
@discussion The default is YES - in other words, cookies are sent from and
stored to the cookie manager by default.
NOTE: In releases prior to 10.3, this value is ignored
*/
@property BOOL HTTPShouldHandleCookies;
这个属性,决定是否对此请求执行默认的cookie处理(如果cookie应与此请求一起发送并设置,则为YES;否则为NO)。默认为YES,换句话说,Cookie默认情况下是从cookie管理器发送并存储到cookie管理器的。 注:在10.3之前的版本中,该值被忽略。
(d)是否使用pipelining
/*!
@abstract Sets whether the request should not wait for the previous response
before transmitting (YES if the receiver should transmit before the previous response is
received. NO to wait for the previous response before transmitting)
@discussion Calling this method with a YES value does not guarantee HTTP
pipelining behavior. This method may have no effect if an HTTP proxy is
configured, or if the HTTP request uses an unsafe request method (e.g., POST
requests will not pipeline). Pipelining behavior also may not begin until
the second request on a given TCP connection. There may be other situations
where pipelining does not occur even though YES was set.
HTTP 1.1 allows the client to send multiple requests to the server without
waiting for a response. Though HTTP 1.1 requires support for pipelining,
some servers report themselves as being HTTP 1.1 but do not support
pipelining (disconnecting, sending resources misordered, omitting part of
a resource, etc.).
*/
设置请求是否在发送之前不等待先前的响应(如果接收器应在接收到前一个响应之前发送,则为YES;
在发送之前等待先前的响应就设置为NO)。以YES值调用此方法并不保证HTTP流水线行为。 如果配置
了HTTP代理,或者HTTP请求使用不安全的请求方法(例如,POST请求不会流水线),则此方法可能不起
作用。 在给定的TCP连接上的第二个请求之前,流水线行为也可能不会开始。 即使设置了YES,也可能会
出现流水线不会发生的其他情况。 HTTP 1.1允许客户端向服务器发送多个请求,而无需等待响应。 尽管HTTP 1.1
需要流水线支持,但有些服务器会将自己报告为HTTP 1.1,但不支持流水线操作(断开连接,发送资源乱序,忽略部分资源等)。
@property BOOL HTTPShouldUsePipelining API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0));
上面给大家翻译的应该很好理解,其实还可以这么理解,通常默认情况下请求和响应是顺序的, 也就是说请求–>得到响应后,再请求。如果将HTTPShouldUsePipelining
设置为YES, 则允许不必等到response
, 就可以再次请求。这个会很大的提高网络请求的效率,但是也可能会出问题。
因为客户端无法正确的匹配请求与响应, 所以这依赖于服务器必须保证,响应的顺序与客户端请求的顺序一致,如果服务器不能保证这一点,那可能导致响应和请求混乱,具体原理图如下所示。
(e)HTTP头区域的设置
/*!
@abstract Sets the HTTP header fields of the receiver to the given
dictionary.
@discussion This method replaces all header fields that may have
existed before this method call.
<p>Since HTTP header fields must be string values, each object and
key in the dictionary passed to this method must answer YES when
sent an <tt>-isKindOfClass:[NSString class]</tt> message. If either
the key or value for a key-value pair answers NO when sent this
message, the key-value pair is skipped.
*/
将接收器的HTTP头字段设置为给定的字典。此方法替换此方法调用之前可能存在的所有头字段。
由于HTTP头字段必须是字符串值,因此传递给此方法的字典中的每个对象和键必须在发送<tt> -isKindOfClass:[NSString类] </ tt>
消息时回答YES。 如果键值对的键或值在发送此消息时回答NO,则会跳过键值对。
@property (nullable, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;
这个属性返回一个包含所有接受者HTTP头区域的字典。
这里进行了判断
if (sself.headersFilter) {
request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = sself.HTTPHeaders;
}
如果SDWebImageDownloaderHeadersFilterBlock这个block不为空,那么就返回一个SDHTTPHeadersDictionary
不可变字典,如果为空就返回一个可变字典@property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders
。
typedef NSDictionary<NSString *, NSString *> SDHTTPHeadersDictionary;
typedef NSMutableDictionary<NSString *, NSString *> SDHTTPHeadersMutableDictionary;
typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterBlock)(NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers);
/**
* Set filter to pick headers for downloading image HTTP request.
*
* This block will be invoked for each downloading image request, returned
* NSDictionary will be used as headers in corresponding HTTP request.
*/
@property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter;
(f)实例化SDWebImageDownloaderOperation并设置是否解压缩图片
SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
operation.shouldDecompressImages = sself.shouldDecompressImages;
这里实例化SDWebImageDownloaderOperation
的时候需要传入上下文session,如下所示。
// The session in which data tasks will run
@property (strong, nonatomic) NSURLSession *session;
并设置是否解压缩图片
/**
* Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
* Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
*/
@property (assign, nonatomic) BOOL shouldDecompressImages;
解压下载和缓存的图像可以提高性能,但会占用大量内存。默认设置为YES。 如果由于内存消耗过多而导致崩溃,请将其设置为NO。
(g)NSURLCredential 身份认证
web 服务可以在返回 http 响应时附带认证要求 challenge,作用是询问 http 请求的发起方是谁,这时发起方应提供正确的用户名和密码(即认证信息),然后 web 服务才会返回真正的 http 响应。
下面看一下身份验证
if (sself.urlCredential) {
operation.credential = sself.urlCredential;
}
else if (sself.username && sself.password) {
operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
}
这里,urlCredential
属性定义如下:
/**
* Set the default URL credential to be set for request operations.
*/
@property (strong, nonatomic, nullable) NSURLCredential *urlCredential;
操作SDWebImageDownloaderOperation
的credential
定义如下:
/**
* The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`.
*
* This will be overridden by any shared credentials that exist for the username or password of the request URL, if present.
*/
@property (nonatomic, strong, nullable) NSURLCredential *credential;
这里的逻辑就是这个验证如果存在就直接通过属性赋值过去,如果不存在那么就直接利用苹果的API直接创建。这里创建需要传入一个用户名、密码还有一个枚举值,下面看一下这个枚举值。
/*!
@enum NSURLCredentialPersistence
@abstract Constants defining how long a credential will be kept around
@constant NSURLCredentialPersistenceNone This credential won't be saved.
@constant NSURLCredentialPersistenceForSession This credential will only be stored for this session.
@constant NSURLCredentialPersistencePermanent This credential will be stored permanently. Note: Whereas in Mac OS X any application can access any credential provided the user gives permission, in iPhone OS an application can access only its own credentials.
@constant NSURLCredentialPersistenceSynchronizable This credential will be stored permanently. Additionally, this credential will be distributed to other devices based on the owning AppleID.
Note: Whereas in Mac OS X any application can access any credential provided the user gives permission, on iOS an application can
access only its own credentials.
*/
@abstract常量定义证书将保留多久
@constant NSURLCredentialPersistenceNone这个证书不会被保存。
@constant NSURLCredentialPersistenceForSession此凭证将仅存储于此会话中。
@constant NSURLCredentialPersistencePermanent这个凭证将永久存储。 注意:在Mac OS X中,任何应用程序都可以访问任何凭据,只要用户授予权限,在iPhone OS中,应用程序只能访问自己的凭据。
@constant NSURLCredentialPersistenceSynchronizable此凭证将永久存储。 此外,此凭证将根据拥有的AppleID分配给其他设备。 注意:在Mac OS X中,任何应用程序都可以访问任何凭据,只要用户授予权限即可,一个iOS程序只能访问自己的凭据。
typedef NS_ENUM(NSUInteger, NSURLCredentialPersistence) {
NSURLCredentialPersistenceNone,
NSURLCredentialPersistenceForSession,
NSURLCredentialPersistencePermanent,
NSURLCredentialPersistenceSynchronizable API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0))
};
(h)队列的优先级
这里也设置了队列的优先级,如下所示:
if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
这里,如果options是SDWebImageDownloaderHighPriority,也就是高优先级,那么就设置NSOperation这个属性@property NSOperationQueuePriority queuePriority;
的优先级为NSOperationQueuePriorityHigh
,否则为NSOperationQueuePriorityLow
,这里看一下这个枚举值。
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
这个很好理解就不多说了。
(i)设置队列的执行顺序
下面就是将操作加入到队列并设置队列的执行顺序,同时为LIFO类型的队列添加依赖,防止出现错乱。
/**
* Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`.
*/
@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;
@property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue;
@property (weak, nonatomic, nullable) NSOperation *lastAddedOperation;
[sself.downloadQueue addOperation:operation];
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;
}
这里队列执行顺序是一个枚举,其实就是队列和堆栈两种队列执行顺序,如下所示:
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
/**
* Default value. All download operations will execute in queue style (first-in-first-out).
默认值,所有下载操作都以队列的形式执行。
*/
SDWebImageDownloaderFIFOExecutionOrder,
/**
* All download operations will execute in stack style (last-in-first-out).
所有下载操作都以堆栈的形式执行。
*/
SDWebImageDownloaderLIFOExecutionOrder
};
这些都设置好了,就可以返回对应的操作对象SDWebImageDownloaderOperation *operation
了。
后记
本篇已结束,后面更精彩~~~