SDWebImage探究(十一) —— 深入研究图片下载流程(五)之SDWebImageDownloadToken和操作对象的生成和返回

版本记录

版本号 时间
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;

操作SDWebImageDownloaderOperationcredential定义如下:

/**
 * 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了。

后记

本篇已结束,后面更精彩~~~

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

推荐阅读更多精彩内容