本部分主要的作用:对UI控件进行网络请求的支持。
UIKit
功能:本部分主要方便UI使用网络功能,为UIImageView,UIButton等设置网络下载图片。功能类似于SDWebImage库。以及UIProgressView,UIWebView,UIRefreshControl,UIActivityIndicatorView部分UI的网络使用封装。
下面分别解析一下各个控件的设计思路
UIImageView+AFNetworking
为UIImageView加载网络图片提供便利的接口。
使用方法
- (void)setImageWithURL:(NSURL *)url;
- (void)setImageWithURL:(NSURL *)url
placeholderImage:(nullable UIImage *)placeholderImage;
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(nullable UIImage *)placeholderImage
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
这里为我们抛出了图片下载地址,默认显示图片,成功的回调,失败的回调。
下面解析下载的核心转换代码:
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure
{
if ([urlRequest URL] == nil) {
[self cancelImageDownloadTask];
self.image = placeholderImage;
return;
}
if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){ // - 检测是当前任务是否有这个urlRequest
return;
}
[self cancelImageDownloadTask];
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
id <AFImageRequestCache> imageCache = downloader.imageCache;
//Use the image from the image cache if it exists
UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
if (cachedImage) {
if (success) {
success(urlRequest, nil, cachedImage);
} else {
self.image = cachedImage;
}
[self clearActiveDownloadInformation];
} else {
if (placeholderImage) {
self.image = placeholderImage;
}
__weak __typeof(self)weakSelf = self;
NSUUID *downloadID = [NSUUID UUID]; // 下载标识符
AFImageDownloadReceipt *receipt; // 完成标识符,下载跟完成是一样的
receipt = [downloader
downloadImageForURLRequest:urlRequest
withReceiptID:downloadID
success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
if (success) { // 成功返回
success(request, response, responseObject);
} else if(responseObject) { // 成功但是不是请求成功,用缓存图片,直接给imageview赋值
strongSelf.image = responseObject;
}
[strongSelf clearActiveDownloadInformation];
}
}
failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
if (failure) {
failure(request, response, error);
}
[strongSelf clearActiveDownloadInformation];
}
}]; // 注意,这个是同步的,所以可以获得receipt
self.af_activeImageDownloadReceipt = receipt;
}
}
首先会对上次的任务进行检测,如果有的话,取消掉。
if (self.af_activeImageDownloadReceipt != nil) {
[[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt]; // runtime 管理对象获取downloader单利,取消当前这个receipt下的任务
[self clearActiveDownloadInformation]; // 清空 af_activeImageDownloadReceipt
}
}
这里检测的是self.af_activeImageDownloadReceipt,注意这是分类中添加的属性,下载的唯一标识符。也就是说如果这个任务已经下载过的话,下次重新下载会取消上次的下载任务。
类别添加属性的方法是用的runtime:
- (AFImageDownloadReceipt *)af_activeImageDownloadReceipt {
return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, @selector(af_activeImageDownloadReceipt));
}
- (void)af_setActiveImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt {
objc_setAssociatedObject(self, @selector(af_activeImageDownloadReceipt), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
检测如果本地有缓存,直接获取缓存的图片,返回
缓存类是:AFAutoPurgingImageCache。
缓存类的封装思路:
AFAutoPurgingImageCache
简单的图片缓存,比起SDWebImage来说比较简单,没有分别存内存和硬盘。
@property (nonatomic, strong) NSMutableDictionary <NSString* , AFCachedImage*> *cachedImages;
@property (nonatomic, assign) UInt64 currentMemoryUsage;
@property (nonatomic, strong) dispatch_queue_t synchronizationQueue;
cachedImages:缓存字典
currentMemoryUsage:当前使用的内存字节大小
synchronizationQueue:同步队列,主要用来做同步操作
初始化方法:
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity {
if (self = [super init]) {
self.memoryCapacity = memoryCapacity;
self.preferredMemoryUsageAfterPurge = preferredMemoryCapacity;
self.cachedImages = [[NSMutableDictionary alloc] init];
NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]];
self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(removeAllImages)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
return self;
}
同步队列是用的自定义并行队列。这里有对内存警告的处理。收到内存警告的通知:UIApplicationDidReceiveMemoryWarningNotification,执行清空所有缓存的图片removeAllImages方法。
添加图片:
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier {
dispatch_barrier_async(self.synchronizationQueue, ^{
AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier];
AFCachedImage *previousCachedImage = self.cachedImages[identifier];
if (previousCachedImage != nil) {
self.currentMemoryUsage -= previousCachedImage.totalBytes;
}
self.cachedImages[identifier] = cacheImage;
self.currentMemoryUsage += cacheImage.totalBytes;
});
dispatch_barrier_async(self.synchronizationQueue, ^{
if (self.currentMemoryUsage > self.memoryCapacity) {
UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge;
NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate"
ascending:YES];
[sortedImages sortUsingDescriptors:@[sortDescriptor]];
UInt64 bytesPurged = 0;
for (AFCachedImage *cachedImage in sortedImages) {
[self.cachedImages removeObjectForKey:cachedImage.identifier];
bytesPurged += cachedImage.totalBytes;
if (bytesPurged >= bytesToPurge) {
break ;
}
}
self.currentMemoryUsage -= bytesPurged;
}
});
}
使用的是GCD的barrier异步方法,在保证队列中前面的任务全部完成的情况下,执行添加方法,并不阻塞之后的任务执行。
- 首先初始化一个AFCachedImage对象,用这个对象存图片,以及一些图片相关的信息。缓存字节数响应的增加调整。如果已经存在的图片进行处理,那么先减去缓存字节数,最后替换图片。
- 对缓存内容大小进行判断,使用barrier可以保证在上个添加任务完成后再执行下个任务。如果缓存大小超过总的缓存容量,那么首先执行排序,然后顺序移除,直到不再超出缓存容量。
移除图片
移除图片类似使用的GCD的barrier同步方法。这里会阻塞,等待移除任务做完之后,进行以后的任务。跟添加有区别,这样可以保证线程安全。
上代码:
- (BOOL)removeImageWithIdentifier:(NSString *)identifier {
__block BOOL removed = NO;
dispatch_barrier_sync(self.synchronizationQueue, ^{
AFCachedImage *cachedImage = self.cachedImages[identifier];
if (cachedImage != nil) {
[self.cachedImages removeObjectForKey:identifier];
self.currentMemoryUsage -= cachedImage.totalBytes;
removed = YES;
}
});
return removed;
}
- (BOOL)removeAllImages {
__block BOOL removed = NO;
dispatch_barrier_sync(self.synchronizationQueue, ^{
if (self.cachedImages.count > 0) {
[self.cachedImages removeAllObjects];
self.currentMemoryUsage = 0;
removed = YES;
}
});
return removed;
}
这里支持单个删除,全部删除。操作的还是当前这个cachedImages字典。
网络下载,并把获取到的图片赋值给当前的ImageView
首先,会先设置一个默认的placeholderImage占位图,如果你设置过的话。
然后,这里使用的是[NSUUID UUID]来做下载的唯一标识符,注意downloadID如receipt是一样的。这个是用来区别是否是当前任务。
下载任务是用的:AFImageDownloader。这是UI下载里面的核心部分。
通过调用下载方法,获取到下载图片结果。
如果成功,并且receiptID匹配当前的下载downloadID,有成功回调的话,返回去成功的参数已经图片。如果没有成功回调的话,直接给uiimageView赋值(这个地方跟SDWebImage还是有区别的,SDWebImage直接赋值,就算有成功回调也会赋值)。然后清空当前的下载信息,其实就是清空这个标示id。
如果失败,如果有失败block的情况,回调回去,没有不做任何处理,相同的清空下载信息。
最后有个赋值:
self.af_activeImageDownloadReceipt = receipt;
这个地方之所以可以这么赋值,是因为下载方法是同步的。注意的是这个任务是同步的,但是发起的网络请求是异步的。
下面解析一下AFImageDownloader.
AFImageDownloader
核心下载类,下载任务,取消任务。
开始下载任务
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
withReceiptID:(nonnull NSUUID *)receiptID
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure {
__block NSURLSessionDataTask *task = nil;
dispatch_sync(self.synchronizationQueue, ^{
NSString *URLIdentifier = request.URL.absoluteString;
if (URLIdentifier == nil) {
if (failure) {
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil];
dispatch_async(dispatch_get_main_queue(), ^{
failure(request, nil, error);
});
}
return;
}
// 1) Append the success and failure blocks to a pre-existing request if it already exists
AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[URLIdentifier];
if (existingMergedTask != nil) {
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure];
[existingMergedTask addResponseHandler:handler];
task = existingMergedTask.task;
return;
}
// 2) Attempt to load the image from the image cache if the cache policy allows it
switch (request.cachePolicy) {
case NSURLRequestUseProtocolCachePolicy:
case NSURLRequestReturnCacheDataElseLoad: // 只有在cache中不存在data时才从原始地址下载。
case NSURLRequestReturnCacheDataDontLoad: { // 只使用cache数据,如果不存在cache,请求失败;用于没有建立网络连接离线模式
UIImage *cachedImage = [self.imageCache imageforRequest:request withAdditionalIdentifier:nil];
if (cachedImage != nil) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
success(request, nil, cachedImage);
});
}
return;
}
break;
}
default:
break;
}
// 3) Create the request and set up authentication, validation and response serialization
NSUUID *mergedTaskIdentifier = [NSUUID UUID];
NSURLSessionDataTask *createdTask;
__weak __typeof__(self) weakSelf = self;
createdTask = [self.sessionManager
dataTaskWithRequest:request
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
dispatch_async(self.responseQueue, ^{
__strong __typeof__(weakSelf) strongSelf = weakSelf;
AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];
if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) {
mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier];
if (error) { // 失败返回回调
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
if (handler.failureBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.failureBlock(request, (NSHTTPURLResponse*)response, error);
});
}
}
} else { // 成功返回回调,添加图片到缓存
[strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil];
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
if (handler.successBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.successBlock(request, (NSHTTPURLResponse*)response, responseObject);
});
}
}
}
}
[strongSelf safelyDecrementActiveTaskCount];
[strongSelf safelyStartNextTaskIfNecessary]; // 成功开启下一个任务。保证最大并发数的情况下。
});
}];
// 4) Store the response handler for use when the request completes
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID
success:success
failure:failure];
AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc]
initWithURLIdentifier:URLIdentifier
identifier:mergedTaskIdentifier
task:createdTask];
[mergedTask addResponseHandler:handler]; // 一个mergedTask添加一个handler,为什么要把handler设计成一个数组呢?
self.mergedTasks[URLIdentifier] = mergedTask;
// 5) Either start the request or enqueue it depending on the current active request count
if ([self isActiveRequestCountBelowMaximumLimit]) { // 不到最大并发数,开启新任务
[self startMergedTask:mergedTask];
} else { // 超出最大并发数,加入缓存队列
[self enqueueMergedTask:mergedTask];
}
task = mergedTask.task; // createdTask
if (task) {
return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task]; // 为task打标签
} else {
return nil;
}
}
首先处理失败的回调,如果失败,抛出失败的request,error,直接返回。
成功的话,分三部分处理
- 判断当前task是否已经存在,如果存在,直接获取这个task,并返回。
- 从缓存里面取,如果已经缓存过,调用成功回调,把缓存的图片返回回去。
- 如果以上两点都没有的,进行网络请求。这里使用的是AFURLSessionManager里面的
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
方法。这个方法的解析参考NSURLSession部分的解析。
回调中,这里会判断是否是当前任务URLIdentifier,如果是,继续处理。并分别返回成功失败的回调。
处理完之后,这里会将队列任务数量-1,然后开启下一个缓存的任务。
[strongSelf safelyDecrementActiveTaskCount];
[strongSelf safelyStartNextTaskIfNecessary];
由于是同步的,所以我们可以获取这个createdTask任务。
初始化一个AFImageDownloaderResponseHandler对象,这个对象主要负责成功失败的回调,以及唯一标示每个任务的id。然后添加到AFImageDownloaderMergedTask的对象中,AFImageDownloaderMergedTask类主要作用:统一管理task的标识符,回调对象。主要这里有两个标识符:
- URLIdentifier:用于标示下载任务唯一。内部使用AFImageDownload使用。
- identifier:用于匹配下载任务与完成的处理唯一。外部类使用。
然后处理缓存队列,如果没有到最大并发数,开启新任务,如果超出最大并发数,加入缓存队列。
看一下入队,出队的代码:
- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask { // 入队: 添加到队列数组中去,所谓的队列和栈,只是数组的顺序而已。
switch (self.downloadPrioritizaton) {
case AFImageDownloadPrioritizationFIFO:
[self.queuedMergedTasks addObject:mergedTask]; // 先进先出,顺序添加到数组
break;
case AFImageDownloadPrioritizationLIFO:
[self.queuedMergedTasks insertObject:mergedTask atIndex:0]; // 先进后出,每次插入到数组第一个位置
break;
}
}
- (AFImageDownloaderMergedTask *)dequeueMergedTask { // 出队
AFImageDownloaderMergedTask *mergedTask = nil;
mergedTask = [self.queuedMergedTasks firstObject];
[self.queuedMergedTasks removeObject:mergedTask]; // 从队列里面移除
return mergedTask; // 返回最先进入队列的任务,先进先出
}
入队的设计还是使用的数组,支持先进先出,后进先出两种入队模式。
取消下载任务
- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt {
dispatch_sync(self.synchronizationQueue, ^{
NSString *URLIdentifier = imageDownloadReceipt.task.originalRequest.URL.absoluteString;
AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];
NSUInteger index = [mergedTask.responseHandlers indexOfObjectPassingTest:^BOOL(AFImageDownloaderResponseHandler * _Nonnull handler, __unused NSUInteger idx, __unused BOOL * _Nonnull stop) {
return handler.uuid == imageDownloadReceipt.receiptID;
}];
if (index != NSNotFound) {
AFImageDownloaderResponseHandler *handler = mergedTask.responseHandlers[index];
[mergedTask removeResponseHandler:handler];
NSString *failureReason = [NSString stringWithFormat:@"ImageDownloader cancelled URL request: %@",imageDownloadReceipt.task.originalRequest.URL.absoluteString];
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey:failureReason};
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];
if (handler.failureBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.failureBlock(imageDownloadReceipt.task.originalRequest, nil, error);
});
}
}
if (mergedTask.responseHandlers.count == 0 && mergedTask.task.state == NSURLSessionTaskStateSuspended) {
[mergedTask.task cancel];
[self removeMergedTaskWithURLIdentifier:URLIdentifier];
}
});
}
- 通过URLIdentifier获取当前mergedTask任务,通过receiptID遍历出当前的这个handler。
- 将handler从mergedTask中移除,抛出失败的回调。
- 最后移除当前的这个mergedTask
UIButton+AFNetworking
封装思路跟UIImageView+AFNetworking基本相同,区别在于图片的处理,UIButton分别处理了图片:
[self setImage:cachedImage forState:state];
背景图片:
[self setBackgroundImage:placeholderImage forState:state];
已经对不同状态的处理。
UIProgressView+AFNetworking
支持下载、上传的进度条,主要使用的技术点是KVO。
设置调用方法:
- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task
animated:(BOOL)animated
{
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
[task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
[self af_setUploadProgressAnimated:animated];
}
- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task
animated:(BOOL)animated
{
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
[task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
[self af_setDownloadProgressAnimated:animated];
}
这里对上传任务的key:state,countOfBytesSent进行了监听,
对下载任务key:state,countOfBytesReceived进行了监听。
支持动画设置:
- (BOOL)af_uploadProgressAnimated {
return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_uploadProgressAnimated)) boolValue];
}
- (void)af_setUploadProgressAnimated:(BOOL)animated {
objc_setAssociatedObject(self, @selector(af_uploadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)af_downloadProgressAnimated {
return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_downloadProgressAnimated)) boolValue];
}
- (void)af_setDownloadProgressAnimated:(BOOL)animated {
objc_setAssociatedObject(self, @selector(af_downloadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
使用runtime的关联对象功能,也是蛮拼的,这么一个小的属性的支持。
核心:kvo的监听回调中
#pragma mark - NSKeyValueObserving
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(__unused NSDictionary *)change
context:(void *)context
{
if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
if ([object countOfBytesExpectedToSend] > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated];
});
}
}
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
if ([object countOfBytesExpectedToReceive] > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated];
});
}
}
if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) {
if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) {
@try {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))];
if (context == AFTaskCountOfBytesSentContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
}
if (context == AFTaskCountOfBytesReceivedContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
}
}
@catch (NSException * __unused exception) {}
}
}
}
}
- 首先对countOfBytesExpectedToSend和countOfBytesExpectedToReceive分别处理:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f,[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f)。两个百分比的计算方式不同。
- 再处理state,如果当前状态是完成:NSURLSessionTaskStateCompleted,移除当前的kvo对象。使用kvo的时候注意移除,否则会导致内存泄露。
UIRefreshControl+AFNetworking
系统的刷新类的支持,使用的是从AFURLSessionManager里抛出的当前task状态通知:
AFNetworkingTaskDidResumeNotification,
AFNetworkingTaskDidSuspendNotification,
AFNetworkingTaskDidCompleteNotification。
- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil];
if (task) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreceiver-is-weak"
#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak"
if (task.state == NSURLSessionTaskStateRunning) {
[self.refreshControl beginRefreshing];
[notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingTaskDidResumeNotification object:task];
[notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidCompleteNotification object:task];
[notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidSuspendNotification object:task];
} else {
[self.refreshControl endRefreshing];
}
#pragma clang diagnostic pop
}
}
主要在resume状态的时候进行刷新动画,在complete、suspend状态下停止刷新动画。
UIActivityIndicatorView+AFNetworking
跟UIRefreshControl封装思路一样。
UIWebView+AFNetworking
支持UIWebView的下载,网络部分使用的是NSURLSession部分的AFHTTPSessionManager的get方法。
- (void)loadRequest:(NSURLRequest *)request
MIMEType:(NSString *)MIMEType
textEncodingName:(NSString *)textEncodingName
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
failure:(void (^)(NSError *error))failure
{
NSParameterAssert(request);
if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) {
[self.af_URLSessionTask cancel];
}
self.af_URLSessionTask = nil;
__weak __typeof(self)weakSelf = self;
NSURLSessionDataTask *dataTask;
dataTask = [self.sessionManager
GET:request.URL.absoluteString
parameters:nil
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (success) {
success((NSHTTPURLResponse *)task.response, responseObject);
}
[strongSelf loadData:responseObject MIMEType:MIMEType textEncodingName:textEncodingName baseURL:[task.currentRequest URL]];
if ([strongSelf.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[strongSelf.delegate webViewDidFinishLoad:strongSelf];
}
}
failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
if (failure) {
failure(error);
}
}];
self.af_URLSessionTask = dataTask;
if (progress != nil) {
*progress = [self.sessionManager downloadProgressForTask:dataTask];
}
[self.af_URLSessionTask resume];
if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
}
请求完成后, 如果成功会:
1)回调成功的数据
2)当前webview加载成功的数据
3)使用代理触发webViewDidStartLoad回调。
失败直接回调回去失败的error。
请求开始后,立刻触发webViewDidStartLoad的回调。对WebView的回调支持比较好。
如果文中有什么错误,欢迎大家指正。
更多问题讨论欢迎加QQ群:200792066
转载请注明出处:http://semyonxu.com
网友评论