美高梅平台下载-美高梅娱乐平台登录

热门关键词: 美高梅平台下载,美高梅娱乐平台登录

方法的声明要如代码中所示的,开始网络请求图

日期:2019-10-05编辑作者:美高梅平台下载

美高梅平台下载 1RCTImage_Sequence_Diagram.jpg

  上篇主要对 NSOperation 进行了拓展学习,下面开始着重学习 SDWebImageDownloaderOperation。

iOS下将照片保存到相册的三种方法

本文是基于 react-native 0.37.0 版本进行分析的。

  • 1、初始化
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
  • 2、通过 setProps(这个 props 就是我们在 JS 里为 <Image> 标签设定的属性值,其中就包括 URI) 调到了setResizeMode:
- setResizeMode:(RCTResizeMode)resizeMode
  • 3、setResizeMode: 调到了 reloadImage
- reloadImage
  • 4、reloadImage 先做了两件事:1、cancelImageLoad。2、imageSourceForSize。
- cancelImageLoad
  • cancelImageLoad里如果有之前保存的 _reloadImageCancellationBlock,则执行该 block,并且置为 nil,同时将 _pendingImageSource 置为 nil。
  • imageSourceForSize,如果传进来的 size 是,直接返回 nil,如果不是,则找一个最合适的 RCTImageSource。
  • 然后 reloadImage 如果获取到了合适的 RCTImageSource 则进入下载流程,否则 clearImage。
if (source && self.frame.size.width > 0 && self.frame.size.height > 0) { // 篇幅原因隐藏了接下来要讲的代码}
  • 定义了三个回调:progressHandler、partialLoadHandler、completionHandler,用于下载进度、部分下载、下载完成调用。将三个 block 作为参数和 source、size、resizeMode 一起通过 imageLoader 的 loadImageVithURLRequest:传递给 imageLoader。并且用 _reloadImageCancellationBlock 接收该方法的返回值(上面提到的 cancelImageLoad 里调用的就是这个 block)
RCTImageLoaderProgressBlock progressHandlerRCTImageLoaderPartialLoadBlock partialLoadHandlerRCTImageLoaderCompletionBlock completionHandler

_reloadImageCancellationBlock = [_bridge.imageLoader loadImageWithURLRequest:source.request size:imageSize scale:imageScale clipped:NO resizeMode:_resizeMode progressBlock:progressHandler partialLoadBlock:partialLoadHandler completionBlock:completionHandler];

注:这里还不知道我们要加载的图片是来自网络还是来自本地(这里是 RN 所有图片的入口)。

  • 5、loadImageWithURLRequest:先定义一个 cancelLoad 回调用于接收 _loadImageOrDataWithURLRequest: 或 decodeImageData: 的返回值。 cancellationBlock 返回给调用方,再定义一个 completionHandler 传递给 _loadImageOrDataWithURLRequest:
__block dispatch_block_t cancelLoaddispatch_block_t cancellationBlockvoid (^completionHandler)(NSError *, id, BOOL, NSString *)
  • cancellationBlock 里调用了上面定义的 cancelLoad 回调,并且在线程锁内修改表示变量 cancelled 的值。
  • completionHandler 里先判断是否被取消或者 self 有没有被释放,然后判断如果 imageData为nil 或者 imageData 已经是 UIImage 类型就直接回调给调用方并且return。completionHandler 接受 4 个参数:

^(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate) {}

  • cacheResult 是 _loadImageOrDataWithURLRequest: 回传过来的表示图片是否有缓存,如果有则查找缓存并对调给调用方并且return,没有则定义一个 decodeCompletionHandler 并且执行 decodeImageData,用刚才定义的 cancelLoad 接收返回值。
cancelLoad = [self _loadImageOrDataWithURLRequest:imageURLRequest size:size scale:scale resizeMode:resizeMode progressBlock:progressBlock partialLoadBlock:partialLoadBlock completionBlock:completionHandler];
  • 6、_loadImageOrDataWithURLRequest 是一个 返回值为 RCTImageLoaderCancellationBlock 的方法。
  • _loadImageOrDataWithURLRequest,先根据 URL 通过 imageURLLoaderForURL: 查找最合适的 id<RCTImageURLLoader> loadHandler (loadHandler 为本地图片加载器,后文讲它是怎么来的),然后也定义了一个 completionHandler,这个 block 接收3个参数:error、imageOrData、fetchDate,在 completionHandler 里判断如果当前线程为主线程并且 imageData 不是 UIImage 类型则扔到全局队列里回调给调用方(即loadImageWithURLRequest:方法的 completionHandler),否则直接回调给调用方。
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForURL:request.URL];void (^completionHandler)(NSError *, id, NSString *)
  • 7、接下来开始加载图片,如果有 loadHandler 则执行本地图片加载,否则执行网络下载。
if (loadHandler) { cancelLoad = [loadHandler loadImageForURL:request.URL size:size scale:scale resizeMode:resizeMode progressHandler:progressHandler partialLoadHandler:partialLoadHandler completionHandler:^(NSError *error, UIImage *image) { completionHandler(error, image, nil); }];} else { // Use networking module to load image cancelLoad = [strongSelf _loadURLRequest:request progressBlock:progressHandler completionBlock:completionHandler];}
  • 8、看网络图片加载部分,先定义一个
RCTURLRequestCompletionBlock processResponse

它接收三个参数:

^(NSURLResponse *response, NSData *data, NSError *error)

processResponse 里面判断网络状态码并且把 data 回调给调用方(_loadImageOrDataWithURLRequest),然后 RCTNetworking 开始网络请求图片。下载完成后就开始了层层回调。

  • 一直回调到步骤 5 这里执行 decodeImageData:
  • 9、completionHandler,先定义一个 completionHandler 接受两个参数(error,image)并且在非主线程队列回调给调用方,然后去查找一个图片 decoder,如果没有则在 _URLRequestQueue 队列里进行解码,解码完成 completionHandler(error,image)。
  • 现在回到步骤 7,看下 loadHandler 是个什么鬼。
 // Find suitable image URL loader id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForURL:request.URL];

可以看到 loadHandler 是这么来的,进去 imageURLLoaderForURL: 方法里发现 loadHandler 是从一个 _loaders 的数组里挑选出来的,_loaders 数组是怎么来的呢,继续看代码:

_loaders = [[_bridge modulesConformingToProtocol:@protocol(RCTImageURLLoader)] sortedArrayUsingComparator:^NSComparisonResult(id<RCTImageURLLoader> a, id<RCTImageURLLoader> b) { float priorityA = [a respondsToSelector:@selector(loaderPriority)] ? [a loaderPriority] : 0; float priorityB = [b respondsToSelector:@selector(loaderPriority)] ? [b loaderPriority] : 0; if (priorityA > priorityB) { return NSOrderedAscending; } else if (priorityA < priorityB) { return NSOrderedDescending; } else { return NSOrderedSame; } }];

关键代码:

[_bridge modulesConformingToProtocol:@protocol(RCTImageURLLoader)]

继续往里跟:

- (NSArray *)modulesConformingToProtocol:(Protocol *)protocol{ NSMutableArray *modules = [NSMutableArray new]; for (Class moduleClass in self.moduleClasses) { if ([moduleClass conformsToProtocol:protocol]) { id module = [self moduleForClass:moduleClass]; if  { [modules addObject:module]; } } } return [modules copy];}

画重点:conformsToProtocol: 方法是检查某个对象以及它的祖先有没有实现某个方法。self.moduleClasses 是个什么东西呢,它是所有遵守了 RCTBridgeModule 协议及 RCTBridgeModule 子孙协议的 Class 的集合。这个集合里面遵守了 RCTImageURLLoader 协议的有两个 RCTLocalAssetImageLoader 和 RCTPhotoLibraryImageLoader。

  通知

方法一

1 extern NSString * _Nonnull const SDWebImageDownloadStartNotification;
2 extern NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification;
3 extern NSString * _Nonnull const SDWebImageDownloadStopNotification;
4 extern NSString * _Nonnull const SDWebImageDownloadFinishNotification;

使用UIImageWriteToSavedPhotosAlbum函数将图片保存到相册,如:

  这 4 个不可变字符串,分别表示:开始下载、接收到数据、停止、完成的通知。

- (void)loadImageFinished:(UIImage *)image

  SDWebImageDownloaderOperationInterface 

{

 1 /**
 2  Describes a downloader operation. If one wants to use a custom downloader op, it needs to inherit from `NSOperation` and conform to this protocol
 3  */
 4 @protocol SDWebImageDownloaderOperationInterface<NSObject>
 5 
 6 - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
 7                               inSession:(nullable NSURLSession *)session
 8                                 options:(SDWebImageDownloaderOptions)options;
 9 
10 - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
11                             completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
12 
13 - (BOOL)shouldDecompressImages;
14 - (void)setShouldDecompressImages:(BOOL)value;
15 
16 - (nullable NSURLCredential *)credential;
17 - (void)setCredential:(nullable NSURLCredential *)value;
18 
19 @end

    UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), (__bridge void *)self);

  描述一个下载操作,如果你想使用一个自定义的下载操作,就需要继承 NSOperation 并且遵守 SDWebImageDownloaderOperationInterface 协议。

}

  6 个代理方法:

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo

  1.使用 NSURLRequest 、NSURLSession 、SDWebImageDownloaderOptions 初始化一个 SDWebImageDownloaderOperation 对象。

{

  2.添加下载进度和下载完成的 block。

    NSLog(@"image = %@, error = %@, contextInfo = %@", image, error, contextInfo);

  3.是否解压图像的 setter 和 getter 方法。

}

  4.是否需要设置凭证的 setter 和 getter 方法。

第一个参数是要保存到相册的图片对象

  SDWebImageDownloaderOperation

第二个参数是保存完成后回调的目标对象

1 @interface SDWebImageDownloaderOperation : NSOperation <SDWebImageDownloaderOperationInterface, SDWebImageOperation, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>

第三个参数就是保存完成后回调到目标对象的哪个方法中,方法的声明要如代码中所示的:

  SDWebImageDownloaderOperation 遵守 SDWebImageDownloaderOperationInterface 、SDWebImageOperation 、NSURLSessionTaskDelegate 、NSURLSessionDataDelegate 协议,首先是上面的 6 个代理方法都要实现。在设计这个 .h 的时候,可以把协议中的方法再次写入这个 .h 中,这样在使用的时候会更加直观。

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo;

1 @property (assign, nonatomic) BOOL shouldDecompressImages;
2 
3 /**
4  * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`.
5  *
6  * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present.
7  */
8 @property (nonatomic, strong, nullable) NSURLCredential *credential;

第四个参数在保存完成后,会原封不动地传回到回调方法的contextInfo参数中。

  通过声明这两个属性,就实现了 SDWebImageDownloaderOperationInterface 协议中的对应的 setter 和 getter 方法对应的代理方法。

方法二

  一般情况下,如果主动指明类的初始化方法,那么肯定会为初始化方法设定几个参数。

使用AssetsLibrary框架中的ALAssetsLibrary类来实现。具体代码如下:

  那么这些参数就应该以只读的方式暴露出来。

- (void)loadImageFinished:(UIImage *)image

  指定初始化方法:

{

 1 /**
 2  *  Initializes a `SDWebImageDownloaderOperation` object
 3  *
 4  *  @see SDWebImageDownloaderOperation
 5  *
 6  *  @param request        the URL request
 7  *  @param session        the URL session in which this operation will run
 8  *  @param options        downloader options
 9  *
10  *  @return the initialized instance
11  */
12 - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
13                               inSession:(nullable NSURLSession *)session
14                                 options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER;

    __block ALAssetsLibrary *lib = [[ALAssetsLibrary alloc] init];

  只读的属性:

    [lib writeImageToSavedPhotosAlbum:image.CGImage metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) {

 1 /**
 2  * The request used by the operation's task.
 3  */
 4 @property (strong, nonatomic, readonly, nullable) NSURLRequest *request;
 5 
 6 /**
 7  * The operation's task
 8  */
 9 @property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask;
10 
11 /**
12  * The SDWebImageDownloaderOptions for the receiver.
13  */
14 @property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options;

        NSLog(@"assetURL = %@, error = %@", assetURL, error);

  其它的属性:

        lib = nil;

1 /**
2  * The expected size of data.
3  */
4 @property (assign, nonatomic) NSInteger expectedSize;
5 
6 /**
7  * The response returned by the operation's connection.
8  */
9 @property (strong, nonatomic, nullable) NSURLResponse *response;

    }];

  表示数据预期的大小和操作连接返回的响应。

}

  返回用于取消此处理程序集的标记 和 取消操作

使用了ALAssetsLibrary类的writeImageToSavedPhotosAlbum:metadata:completionBlock:方法实现。其中第一个参数是一个CGImageRef的对象,表示要传入的图片。第二个参数是图片的一些属性,这里没有设置所以传入nil。最后一个completionBlock是保存完成后的回调,在这个回调中可以取到保存后的图片路径以及保存失败时的错误信息。

 1 /**
 2  *  Adds handlers for progress and completion. Returns a tokent that can be passed to -cancel: to cancel this set of
 3  *  callbacks.
 4  *
 5  *  @param progressBlock  the block executed when a new chunk of data arrives.
 6  *                        @note the progress block is executed on a background queue
 7  *  @param completedBlock the block executed when the download is done.
 8  *                        @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue
 9  *
10  *  @return the token to use to cancel this set of handlers
11  */
12 - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
13                             completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
14 
15 /**
16  *  Cancels a set of callbacks. Once all callbacks are canceled, the operation is cancelled.
17  *
18  *  @param token the token representing a set of callbacks to cancel
19  *
20  *  @return YES if the operation was stopped because this was the last token to be canceled. NO otherwise.
21  */
22 - (BOOL)cancel:(nullable id)token;

注意:使用该类时需要导入AssetsLibrary.framework。而且该类需要在iOS4.0以上可以使用,但是在iOS9.0之后就被标记为过时方法。官方建议使用Photos.framework中的PHPhotoLibrary进行代替,也就是下面所说的第三种方法。

  这个方法不是取消任务的,而是取消任务中的响应,当任务中没有响应者的时候,任务会被取消。

方法三

  SDWebImageDownloaderOperation.m

使用Photos框架的PHPhotoLibrary类来实现保存到相册功能。代码如下:

1 NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification";
2 NSString *const SDWebImageDownloadReceiveResponseNotification = @"SDWebImageDownloadReceiveResponseNotification";
3 NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification";
4 NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinishNotification";

- (void)loadImageFinished:(UIImage *)image

  4 个不可变的通知的字符串的初始化。

{

1 static NSString *const kProgressCallbackKey = @"progress";
2 static NSString *const kCompletedCallbackKey = @"completed";

    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{

  定义两个静态不可变的字符串,表示下载进度回调的 key 和下载完成的回调的 key。

        /写入图片到相册

1 typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;

        PHAssetChangeRequest *req = [PHAssetChangeRequest creationRequestForAssetFromImage:image]美高梅娱乐平台登录,;

  定义一个 key 是字符串 value 是 id 的可变的字典类型。( SDCallbacksDictionary)

    } completionHandler:^(BOOL success, NSError * _Nullable error) {

 1 @interface SDWebImageDownloaderOperation ()
 2 
 3 @property (strong, nonatomic, nonnull) NSMutableArray<SDCallbacksDictionary *> *callbackBlocks;
 4 
 5 @property (assign, nonatomic, getter = isExecuting) BOOL executing;
 6 @property (assign, nonatomic, getter = isFinished) BOOL finished;
 7 @property (strong, nonatomic, nullable) NSMutableData *imageData;
 8 
 9 // This is weak because it is injected by whoever manages this session. If this gets nil-ed out, we won't be able to run
10 // the task associated with this operation
11 @property (weak, nonatomic, nullable) NSURLSession *unownedSession;
12 // This is set if we're using not using an injected NSURLSession. We're responsible of invalidating this one
13 @property (strong, nonatomic, nullable) NSURLSession *ownedSession;
14 
15 @property (strong, nonatomic, readwrite, nullable) NSURLSessionTask *dataTask;
16 
17 @property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue;
18 
19 #if SD_UIKIT
20 @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;
21 #endif
22 
23 @end
24 
25 @implementation SDWebImageDownloaderOperation {
26     size_t width, height;
27 #if SD_UIKIT || SD_WATCH
28     UIImageOrientation orientation;
29 #endif
30     BOOL responseFromCached;
31 }
32 
33 @synthesize executing = _executing;
34 @synthesize finished = _finished;

        NSLog(@"success = %d, error = %@", success, error);

   callbackBlocks 是一个可变数组,数组里面存放的是  SDCallbacksDictionary,这是上面定义的一个可变字典的类型。这个字典的 key 是字符串,这个字符串有种情况,就是上面定义的两个静态不可变字符串:kProgressCallbackKey 和 kCompletedCallbackKey,也就是说进度和完成的回调都放在一个数组里,字典的 value 就是回调的 block。

    }];

  unownedSession 这个属性是初始化时候传进来的参数,作者提到,这个参数不一定是可用的,也就是说是不安全的,当出现不可用的情况的时候,就需要使用 ownedSession

}

  responseFromCached  用于设置是否需要缓存响应,默认是 YES。 

该例子中先调用PHPhotoLibrary类的performChanges:completionHandler:方法,然后在它的changeBlock中,通过PHAssetChangeRequest类的creationRequestForAssetFromImage:方法传入一个图片对象即可实现保存到相册的功能。然后completionHandler中会告诉我们是否操作成功。

  barrierQueue 是一个 GCD 队列。

进阶使用:得到保存到相册的图片对象

  backgroundTaskId 是在 app 进入后台后申请的后台任务的身份。 

也许会有人需要在保存相册后得到图片的PHAsset对象来进行后续操作(昨天刚好碰到有朋友遇到这样的问题)。那么,这里对上面例子进行改进,在创建PHAssetChangeRequest后将它的placeholderForCreatedAsset属性的localIdentifier属性保存到一个数组中,等待操作完成后再通过这个数组来查找刚刚添加的图片对象。请看下面栗子:

  初始化一个任务 

- (void)loadImageFinished:(UIImage *)image

 1 - (nonnull instancetype)init {
 2     return [self initWithRequest:nil inSession:nil options:0];
 3 }
 4 
 5 - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
 6                               inSession:(nullable NSURLSession *)session
 7                                 options:(SDWebImageDownloaderOptions)options {
 8     if ((self = [super init])) {
 9         _request = [request copy];
10         _shouldDecompressImages = YES;
11         _options = options;
12         _callbackBlocks = [NSMutableArray new];
13         _executing = NO;
14         _finished = NO;
15         _expectedSize = 0;
16         _unownedSession = session;
17         responseFromCached = YES; // Initially wrong until `- URLSession:dataTask:willCacheResponse:completionHandler: is called or not called
18         _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
19     }
20     return self;
21 }
22 
23 - (void)dealloc {
24     SDDispatchQueueRelease(_barrierQueue);
25 }

{

  指定初始化方法实现,主要给属性赋值。

    NSMutableArray *imageIds = [NSMutableArray array]美高梅平台下载,;

  添加响应者

        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{

 1 - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
 2                             completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
 3     SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
 4     if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
 5     if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
 6     dispatch_barrier_async(self.barrierQueue, ^{
 7         [self.callbackBlocks addObject:callbacks];
 8     });
 9     return callbacks;
10 }

            //写入图片到相册

  把指定 key 和 指定 value 的字典添加到 _callbackBlocks 数组。

            PHAssetChangeRequest *req = [PHAssetChangeRequest creationRequestForAssetFromImage:image];

  dispatch_barrier_async

            //记录本地标识,等待完成后取到相册中的图片对象

  我们可以创建两种类型的队列,串行和并行,也就是:

            [imageIds addObject:req.placeholderForCreatedAsset.localIdentifier];

  DISPATCH_QUEUE_SERIAL 、DISPATCH_QUEUE_CONCURRENT。那么 dispatch_barrier_asyncdispatch_barrier_sync 有什么不同之处:

        } completionHandler:^(BOOL success, NSError * _Nullable error) {

  barrier 这个词是栅栏的意思,也就是说是用来做拦截功能的,上边的这两种都能够拦截任务,换句话说,就是只有当前任务完成后,队列后边的任务才能完成。

            NSLog(@"success = %d, error = %@", success, error);

  不同之处就是,dispatch_barrier_sync 控制了任务往队列添加这一过程,只有当前任务完成之后,才能往队列中添加任务。dispatch_barrier_async 不会控制队列添加任务,但是只有当前任务完成后,队列中后边的任务才会执行。

            if (success)

  那么在这里的任务是往数组中添加数据,对顺序没什么要求,采取 dispatch_barrier_async 就可以,已经能保证数据添加的安全性了。

            {

 

                //成功后取相册中的图片对象

1 - (nullable NSArray<id> *)callbacksForKey:(NSString *)key {
2     __block NSMutableArray<id> *callbacks = nil;
3     dispatch_sync(self.barrierQueue, ^{
4         // We need to remove [NSNull null] because there might not always be a progress block for each callback
5         callbacks = [[self.callbackBlocks valueForKey:key] mutableCopy];
6         [callbacks removeObjectIdenticalTo:[NSNull null]];
7     });
8     return [callbacks copy];    // strip mutability here
9 }

                __block PHAsset *imageAsset = nil;

  我们需要删除 [NSNull null],因为每次回调可能不总是有一个回调 block。

                PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:imageIds options:nil];

  在 self.barrierQueue 队列里面同步执行。

                [result enumerateObjectsUsingBlock:^(PHAsset * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

  self.callbackBlocks 是一个数组,但是这里从数组里面取 block 采用了:

                    imageAsset = obj;

1 callbacks = [[self.callbackBlocks valueForKey:key] mutableCopy];

1 @interface NSDictionary<KeyType, ObjectType>(NSKeyValueCoding)
2 
3 /* Return the result of sending -objectForKey: to the receiver.
4 */
5 - (nullable ObjectType)valueForKey:(NSString *)key;
6 
7 @end

                    *stop = YES;

  假定 self.callbackBlocks 的结构是这样的:

                }];

1 @[@{@"completed" : Block1}, 
2 @{@"progress" : Block2}, 
3 @{@"completed" : Block3}, 
4 @{@"progress" : Block4}, 
5 @{@"completed" : Block5}, 
6 @{@"progress" : Block6}]

                if (imageAsset)

   那么得到的如果 key 是 @"progress" 则 callbacks 就是 [Block2, Block4, Block6]。

                {

1 - (void)removeObjectIdenticalTo:(ObjectType)anObject;

                    //加载图片数据

  这个方法会移除数组中和 anObject 相同地址的元素。

                    [[PHImageManager defaultManager] requestImageDataForAsset:imageAsset

1 [callbacks removeObjectIdenticalTo:[NSNull null]];

                          options:nil

  移除 callbacks 里面的 [NSNull null]。

                          resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {

 1 - (BOOL)cancel:(nullable id)token {
 2     __block BOOL shouldCancel = NO;
 3     dispatch_barrier_sync(self.barrierQueue, ^{
 4         [self.callbackBlocks removeObjectIdenticalTo:token];
 5         if (self.callbackBlocks.count == 0) {
 6             shouldCancel = YES;
 7         }
 8     });
 9     if (shouldCancel) {
10         [self cancel];
11     }
12     return shouldCancel;
13 }

                              NSLog("imageData = %@", imageData);

   布尔 shouldCancel 表示是否取消操作。

                          }];

  在 self.barrierQueue 同步删除 self.callbackBlocks 里面的指定回调。(typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;)

                }

  当 self.callbackBlocks 里面的回调删除完的时候,取消操作。

            }

  开启下载任务

        }];

 1 - (void)start {
 2     @synchronized (self) {
 3         if (self.isCancelled) {
 4             self.finished = YES;
 5             [self reset];
 6             return;
 7         }
 8 
 9 #if SD_UIKIT
10         Class UIApplicationClass = NSClassFromString(@"UIApplication");
11         BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];
12         if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
13             __weak __typeof__ (self) wself = self;
14             UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
15             self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
16                 __strong __typeof (wself) sself = wself;
17 
18                 if (sself) {
19                     [sself cancel];
20 
21                     [app endBackgroundTask:sself.backgroundTaskId];
22                     sself.backgroundTaskId = UIBackgroundTaskInvalid;
23                 }
24             }];
25         }
26 #endif
27         NSURLSession *session = self.unownedSession;
28         if (!self.unownedSession) {
29             NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
30             sessionConfig.timeoutIntervalForRequest = 15;
31             
32             /**
33              *  Create the session for this task
34              *  We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
35              *  method calls and completion handler calls.
36              */
37             self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
38                                                               delegate:self
39                                                          delegateQueue:nil];
40             session = self.ownedSession;
41         }
42         
43         self.dataTask = [session dataTaskWithRequest:self.request];
44         self.executing = YES;
45     }
46     
47     [self.dataTask resume];
48 
49     if (self.dataTask) {
50         for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
51             progressBlock(0, NSURLResponseUnknownLength, self.request.URL);
52         }
53         dispatch_async(dispatch_get_main_queue(), ^{
54             [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
55         });
56     } else {
57         [self callCompletionBlocksWithError:[NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}]];
58     }
59 
60 #if SD_UIKIT
61     Class UIApplicationClass = NSClassFromString(@"UIApplication");
62     if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
63         return;
64     }
65     if (self.backgroundTaskId != UIBackgroundTaskInvalid) {
66         UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)];
67         [app endBackgroundTask:self.backgroundTaskId];
68         self.backgroundTaskId = UIBackgroundTaskInvalid;
69     }
70 #endif
71 }

}

  开启下载任务,首先判断该任务是否已经被设置取消了,如果被设置取消了,那么就不需要开启下载任务了,并重置,并设置 finished 为 YES(通过 KVO 设置)。

总结

1 - (void)setFinished:(BOOL)finished {
2     [self willChangeValueForKey:@"isFinished"];
3     _finished = finished;
4     [self didChangeValueForKey:@"isFinished"];
5 }

第一种方式是最常用的,使用起来很方便,传入UIImage就可以了,也不需要担心iOS不同版本的问题。唯一缺点就是无法找到对应添加的图片。

 

第二种方式是iOS4之后加入的,在iOS9后又不推荐使用了。他也提供了很直观的方式来保存图片,并且也能够取到保存后相对应的图片路径。

  重置操作:

第三种方式是iOS8之后加入的,他的使用稍微复杂一点,但是它允许进行批量的操作,例如添加、修改、删除等。如果要做更近复杂的操作的话,这种方式是比较推荐的方式。

 1 - (void)reset {
 2     dispatch_barrier_async(self.barrierQueue, ^{
 3         [self.callbackBlocks removeAllObjects];
 4     });
 5     self.dataTask = nil;
 6     self.imageData = nil;
 7     if (self.ownedSession) {
 8         [self.ownedSession invalidateAndCancel];
 9         self.ownedSession = nil;
10     }
11 }

看到好的,粘贴下来。简友帮忙监督

  

  是否开启后台下载任务

  条件 1:

  判断 self.options (下载设置)是否在 app 进入后台后继续未完成的下载(如果后台任务过期,操作将被取消):

本文由美高梅平台下载发布于美高梅平台下载,转载请注明出处:方法的声明要如代码中所示的,开始网络请求图

关键词:

1怎样学习C语言,算法是程序设计的基础美高梅平

C语言是面向过程的,而C++是面向对象的 C和C++的区别: 1怎样学习C语言? 1怎样学习C语言? C是一个结构化语言,...

详细>>

小编推荐一个学C语言/C++的学习裙二六三,C程序

C语言是面向过程的,而C++是面向对象的 第一章:编译器和程序 C和C++的区别: 第一章:编译器和程序 很多小伙伴...

详细>>

【美高梅平台下载】在C中更多的是算法的概念,

可能大家不太理解这是什么意思,有几个概念我解释一下,返回值和参数?这两个是相对的,返回值就是这个函数返...

详细>>

请在评论区留言@作者,C程序的设计首要考虑的是

C语言是面向过程的,而C++是面向对象的 图片发自简书App 1.关键字的基本概念 1.1特征:在xcode中会显示特殊颜色;全部...

详细>>