#前言:AFNetworking是一款很是有名网络框架,笔者也是用了挺久了,可是也是仅限于使用一些方法。最近一直在思考,要想成为一名优秀的开发者,确定不能仅仅停留在会用的阶段。因此痛定思痛,此次下定决心,本身的第一遍博客就从AFN开始。json
下面我会按着模块来分析:设计模式
#通讯模块xcode
##先从NSURLSession开始分析:安全
@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>
复制代码
打开AFHTTPSessionManager,首先就能明白AFHTTPSessionManager继承自AFURLSessionManager,属于这个通讯模块的最上层。bash
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
- (instancetype)init {
return [self initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {
return [self initWithBaseURL:url sessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
return [self initWithBaseURL:nil sessionConfiguration:configuration];
}
复制代码
而在其.m中看看到初始化,[AFHTTPSessionManager manager]也就是咱们平常调用AFN开始网络通讯的初始化。其用的设计模式就是工厂模式。网络
*/
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
//调用父类初始化方法
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
/*
为了确保NSURL +URLWithString:relativeToURL: works能够正确执行,在baseurlpath的最后添加‘/’
*/
//url有值且没有‘/’,那么在url的末尾添加‘/’
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
//给requestSerializer、responseSerializer设置默认值
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
复制代码
其中self = [super initWithSessionConfiguration:configuration];会去调用父类(AFURLSessionManager)的initWithSessionConfiguration的初始化方法,进行一些url初始化的配置session
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
//设置默认的configuration,配置咱们的session
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
//持有configuration
self.sessionConfiguration = configuration;
//设置为delegate的操做队列并发的线程数量1,也就是串行队列
//避免了线程的不安全,
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
/*
-若是完成后须要作复杂(耗时)的处理,能够选择异步队列
-若是完成后直接更新UI,能够选择主队列
[NSOperationQueue mainQueue]
*/
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
//默认为json解析
self.responseSerializer = [AFJSONResponseSerializer serializer];
//设置默认证书 无条件信任证书https认证
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
//网络状态监听
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
//delegate= value taskid = key
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
//使用NSLock确保线程安全
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//异步的获取当前session的全部未完成的task。
//后台任务从新回来初始化session,可能就会有先前的任务
//保证剩余的操做不会被浪费掉
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
return self;
}
复制代码
以上就是AFN在初始化的网络模块运行的方法 其核心全局的session也是在URLSessionManager中建立出来并发
AFHTTPSessionManager做为一个供开发者调用的模块,其.h暴露出来的API都是简单明了,只须要提供必要的URL和params就能够经过block返回结果。框架
下面咱们先简单看下ANF在请求的过程当中的流程异步
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
//返回一个task,而后开始网络请求
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
//开始网络请求
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *) method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
/*
1.先调用AFHTTPRequestSerializer的requestWithMethod函数构建request
2.处理request构建产生的错误 – serializationError
//relativeToURL表示将URLString拼接到baseURL后面
*/
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
//http://fuckingclangwarnings.com/#semantic
//xcode忽略编译器的警告,diagnostic:诊断的
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//completionQueue不存在返回dispatch_get_main_queue
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
#pragma clang diagnostic pop
}
return nil;
}
//此时的request已经将参数拼接在url后面
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
复制代码
不论是调用GET/POST(除了表单,可是表单是调用一个返回NSURLSessionDataTask的方法)/DELETE/PATCH/PUT,都会调用dataTaskWithHTTPMethod
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
复制代码
在上面的方法中,生成了一个request对象
//使用session和request来建立一个NSURLSessionDataTask对象
dataTask = [self.session dataTaskWithRequest:request];
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
复制代码
经过session和request来生成一个task
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
[self setDelegate:delegate forTask:dataTask];
复制代码
而task和delegate绑定在一块儿
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
//task和delegate都不能为空
NSParameterAssert(task);
NSParameterAssert(delegate);
//加锁确保中间代码块是原子操做,线程安全
[self.lock lock];
//将delegate存入字典,以taskid做为key,说明每一个task都有各自的代理
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
//设置两个NSProgress的变量 - uploadProgress和downloadProgress,给session task添加了两个KVO事件
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
- (void)setupProgressForTask:(NSURLSessionTask *)task {
...
//给task和progress添加kvo
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
options:NSKeyValueObservingOptionNew
context:NULL];
...
}
//在KVO回调方法把结果回调出去
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
...
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
...
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
...
}
复制代码
从以上能够看出:session是惟一的 request和task和delegate是一一对应的,delegate承担着数据处理并转发的功能。
该模块中的一些重点的功能:
//-若是完成后须要作复杂(耗时)的处理,能够选择异步队列
//-若是完成后直接更新UI,能够选择主队列
// [NSOperationQueue mainQueue]
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
复制代码
delegate在哪一个OperationQueue回调,若是咱们将其设置为[NSOperationQueue mainQueue]就能在主线程进行回调很是的方便。可是不能把耗时操做放在主线程,因此建立了一个异步的串行队列。
//delegate= value taskid = key
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
//使用NSLock确保mutableTaskDelegatesKeyedByTaskIdentifier线程安全
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//再字典的set get remove中加锁,确保线程安全
复制代码
思考🤔: NSlock能够不能够换成信号量,信号量的效率会高一点
#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define UNLOCK(lock) dispatch_semaphore_signal(lock);
@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock;
.....
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
...
LOCK(self.weakCacheLock);
[self.weakCache setObject:obj forKey:[[key mutableCopy] copy]];
UNLOCK(self.weakCacheLock);
...
}
- (void)removeObjectForKey:(id)key {
...
// Remove weak cache
LOCK(self.weakCacheLock);
[self.weakCache removeObjectForKey:key];
UNLOCK(self.weakCacheLock);
}
- (id)objectForKey:(id)key {
...
LOCK(self.weakCacheLock);
obj = [self.weakCache objectForKey:key];
UNLOCK(self.weakCacheLock);
...
}
复制代码
而另外一款优秀的图片库SDWebImage中用信号量保证线程的安全
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
options:NSKeyValueObservingOptionNew
context:NULL];
复制代码
反射机制(对于任意一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为反射机制。) 在delegate的setupProgressForTask中运用反射机制给task添加KVO
@implementation _AFURLSessionTaskSwizzling
+ (void)load {
// 1) 首先构建一个NSURLSession对象session,再经过session构建出一个_NSCFLocalDataTask变量
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
// 2) 获取到af_resume实现的指针
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
// 3) 检查当前class是否实现了resume。若是实现了,继续第4步
while (class_getInstanceMethod(currentClass, @selector(resume))) {
// 4) 获取到当前class的父类(superClass)
Class superClass = [currentClass superclass];
// 5) 获取到当前class对于resume实现的指针
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
// 6) 获取到父类对于resume实现的指针
IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
// 7) 若是当前class对于resume的实现和父类不同(相似iOS7上的状况),而且当前class的resume实现和af_resume不同,才进行method swizzling。
if (classResumeIMP != superclassResumeIMP &&
originalAFResumeIMP != classResumeIMP) {
[self swizzleResumeAndSuspendMethodForClass:currentClass];
}
// 8) 设置当前操做的class为其父类class,重复步骤3~8
currentClass = [currentClass superclass];
}
[localDataTask cancel];
[session finishTasksAndInvalidate];
}
}
...
- (void)af_resume {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_resume];
// 由于通过method swizzling后,此处的af_resume其实就是以前的resume,因此此处调用af_resume就是调用系统的resume。可是在程序中咱们仍是得使用resume,由于其实际调用的是af_resume
// 若是以前是其余状态,就变回resume状态,此处会通知调用taskDidResume
if (state != NSURLSessionTaskStateRunning) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
}
}
...
@end
复制代码
其实看类的名字就能够知道是使用methodswizzle这种黑魔法实现AOP模式 经过调用resume,发起网络请求,同时发送一个通知到sessionManamer,由它统一发送一个状态到UIKIT类里面,来改变视图的变化。