UIKit+AFNetworking
接下来我们再看一遍UIKit+AFNetworking
这一部分。
在AppDelegate中,如果需要状态栏的菊花,那么在didFinishLaunchingWithOptions
中加上[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
方法。
AFNetworkActivityIndicatorManager用于管理状态栏显示的网络请求指示器。
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.currentState = AFNetworkActivityManagerStateNotActive;
//在init方法中通过监听通知的方式控制显示,内部使用runtime已经替换了
//开始的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingTaskDidResumeNotification object:nil];
//暂停的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidSuspendNotification object:nil];
//完成的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidCompleteNotification object:nil];
self.activationDelay = kDefaultAFNetworkActivityManagerActivationDelay;
self.completionDelay = kDefaultAFNetworkActivityManagerCompletionDelay;
return self;
}
//对应得到通知请求开始
- (void)networkRequestDidStart:(NSNotification *)notification {
if ([AFNetworkRequestFromNotification(notification) URL]) {
//判断是否是原请求,是则活跃计数➕1
[self incrementActivityCount];
}
}
//请求结束
- (void)networkRequestDidFinish:(NSNotification *)notification {
if ([AFNetworkRequestFromNotification(notification) URL]) {
//否则➖1
[self decrementActivityCount];
}
}
- (void)incrementActivityCount {
//手动添加KVO属性
[self willChangeValueForKey:@"activityCount"];
//由于可能会多个线程同时操作,需要加线程安全操作
@synchronized(self) {
_activityCount++;
}
[self didChangeValueForKey:@"activityCount"];
dispatch_async(dispatch_get_main_queue(), ^{
//回主线程更新UI
[self updateCurrentStateForNetworkActivityChange];
});
}
#pragma mark - Internal State Management
- (void)setCurrentState:(AFNetworkActivityManagerState)currentState {
@synchronized(self) {
if (_currentState != currentState) {
//手动监听currentState
[self willChangeValueForKey:@"currentState"];
_currentState = currentState;
switch (currentState) {
//根据不同情况,如果是开始下载则开启定时器;如果是结束则暂停定时器并对应设置`[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:networkActivityIndicatorVisible]`
case AFNetworkActivityManagerStateNotActive:
[self cancelActivationDelayTimer];
[self cancelCompletionDelayTimer];
[self setNetworkActivityIndicatorVisible:NO];
break;
case AFNetworkActivityManagerStateDelayingStart:
[self startActivationDelayTimer];
break;
case AFNetworkActivityManagerStateActive:
[self cancelCompletionDelayTimer];
[self setNetworkActivityIndicatorVisible:YES];
break;
case AFNetworkActivityManagerStateDelayingEnd:
[self startCompletionDelayTimer];
break;
}
[self didChangeValueForKey:@"currentState"];
}
}
}
UIImageView+AFNetworking
再来看下[self.imageView setImageWithURL:_post.user.avatarImageURL placeholderImage:[UIImage imageNamed:@"profile-image-placeholder"]];
设置图片,和SD几乎是一样的。
- (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
{
//如果请求的url为空,取消下载任务,并将placeholderImage设置为image
if ([urlRequest URL] == nil) {
[self cancelImageDownloadTask];
self.image = placeholderImage;
return;
}
// 如果请求的url是正在处理的task也返回
if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){
return;
}
//否则取消掉之前的task和af_activeImageDownloadReceipt
[self cancelImageDownloadTask];
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
id <AFImageRequestCache> imageCache = downloader.imageCache;
//Use the image from the image cache if it exists,根据request.URL.absoluteString为key去cachedImages中取,如果存在本次设置结束(注:3.x后并未从disk中取,只是在内存中取)
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;
}
//如果不存在,则先设置placeholderImage,设置ReceiptID为[NSUUID UUID]再去下载,下载就不详细展开了,下载完成之后会根据缓存策略将其key为request.URL存入到cachedImages
__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) {
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];
}
}];
self.af_activeImageDownloadReceipt = receipt;
}
}
- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt {
dispatch_sync(self.synchronizationQueue, ^{
//拿到需要取消的task的请求url
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;
}];
//从AFImageDownloaderMergedTask的responseHandlers中遍历receiptID,如果找到了则先移除这个AFImageDownloaderResponseHandler,并发送一个failureBlock
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);
});
}
}
//如果存在则把task cancel掉,并且根据URLIdentifier为key把task从mergedTasks中移除
if (mergedTask.responseHandlers.count == 0 && mergedTask.task.state == NSURLSessionTaskStateSuspended) {
[mergedTask.task cancel];
[self removeMergedTaskWithURLIdentifier:URLIdentifier];
}
});
}
UIRefreshControl+AFNetworking
再来看看UIRefreshControl+AFNetworking的实现,一句代码便能够集成[self.refreshControl setRefreshingWithStateOfTask:task];
- (AFRefreshControlNotificationObserver *)af_notificationObserver {
AFRefreshControlNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver));
//objc_getAssociatedObject获取AFRefreshControlNotificationObserver实例,如果不存在就创建一个并且objc_setAssociatedObject
if (notificationObserver == nil) {
notificationObserver = [[AFRefreshControlNotificationObserver alloc] initWithActivityRefreshControl:self];
objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return notificationObserver;
}
- (void)setAnimatingWithStateOfTask:(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];
//先将之前的noti移除,如果task.state等于NSURLSessionTaskStateCompleted是接受不到任何其他代理通知的,所以当不等于时addObserver,并且根据NSURLSessionTaskStateRunning开始动画,NSURLSessionTaskStateSuspended或者NSURLSessionTaskStateCanceling结束动画,而至于为什么能收到通知,就是之前说过的runtime `method_exchangeImplementations`替换掉了resume和suspend方法
if (task) {
if (task.state != NSURLSessionTaskStateCompleted) {
UIActivityIndicatorView *activityIndicatorView = self.activityIndicatorView;
if (task.state == NSURLSessionTaskStateRunning) {
[activityIndicatorView startAnimating];
} else {
[activityIndicatorView stopAnimating];
}
[notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task];
[notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task];
[notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidSuspendNotification object:task];
}
}
}
UIWebView+AFNetworking
最后再来看下UIWebView+AFNetworking
- (void)loadRequest:(NSURLRequest *)request
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success
failure:(void (^)(NSError *error))failure
{
[self loadRequest:request MIMEType:nil textEncodingName:nil progress:progress success:^NSData *(NSHTTPURLResponse *response, NSData *data) {
//如果textEncodingName存在并且有效则使用指定的编码,否则默认NSUTF8StringEncoding
NSStringEncoding stringEncoding = NSUTF8StringEncoding;
if (response.textEncodingName) {
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
if (encoding != kCFStringEncodingInvalidId) {
stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding);
}
}
NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding];
if (success) {
string = success(response, string);
}
return [string dataUsingEncoding:stringEncoding];
} failure:failure];
}
- (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);
//使用objc_getAssociatedObject和objc_setAssociatedObject设置NSURLSessionDataTask属性
if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) {
//如果state为NSURLSessionTaskStateRunning或者NSURLSessionTaskStateSuspended则先取消task,将原task置空
[self.af_URLSessionTask cancel];
}
self.af_URLSessionTask = nil;
//同理,创建一个AFHTTPSessionManager属性,使用`dataTaskWithRequest`请求网络;如果progress存在,则传入指定对应的task的下载progress;如果能够响应webViewDidStartLoad方法,则执行;请求成功之后根据MIMEType和textEncodingName设置webview的加载方式将responseObject加载出来,并且如果实现了webViewDidFinishLoad回调方法,则响应webViewDidFinishLoad;实际就是模拟了webview的实现方式!
__weak __typeof(self)weakSelf = self;
__block NSURLSessionDataTask *dataTask;
dataTask = [self.sessionManager
dataTaskWithRequest:request
uploadProgress:nil
downloadProgress:nil
completionHandler:^(NSURLResponse * _Nonnull response, id _Nonnull responseObject, NSError * _Nullable error) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (error) {
if (failure) {
failure(error);
}
} else {
if (success) {
success((NSHTTPURLResponse *)response, responseObject);
}
[strongSelf loadData:responseObject MIMEType:MIMEType textEncodingName:textEncodingName baseURL:[dataTask.currentRequest URL]];
if ([strongSelf.delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[strongSelf.delegate webViewDidFinishLoad:strongSelf];
}
}
}];
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];
}
}