Github OC语言排第一, 其凝聚了众多大神的智慧,无论是在技术点上,还是架构设计上、问题处理方式上,都具有很高的学习价值。
大致结构如下:
核心类就是AFURLSessionManager
了, 管理所有task、证书验证、网络状态、request和response处理。
Task分为4类,分别对应4个Protocol
。根据每个task的属性生成一个AFURLSessionManagerTaskDelegate
代理对象存储在mutableTaskDelegatesKeyedByTaskIdentifierdic
字典中,key为task的ID,即{taskId-delegate}
。并在代理回调中根据taskId
取出delegate
执行相应的代理方法。
基本实现了NSURLSession
的所有代理方法(NSURLSessionDelegate
、NSURLSessionTaskDelegate
、NSURLSessionDataDelegate
、NSURLSessionDownloadDelegate
)。
AFURLSessionManagerTaskDelegate
实现。NSOperationQueue
: 设置线程最大并发数为 1实现串行,代理回调:异步+串行队列AFHTTPSessionManager
继成自AFURLSessionManager
,负责创建Get/Head/Post/Put/Patch/Delete
请求,负责管理requestSerializer
(请求序化)和responseSerializer
(响应序列化)
都遵循AFURLRequestSerialization
协议:请求参数序列化
都遵循AFURLResponseSerialization
协议:验证返回数据,反序列化
application/x-www-form-urlencoded
,也就是 key-value 形式的 url 编码字符串application/xml
,text/xml
application/x-plist
:苹果的 plist 格式方法里处理的东西,可以下载Demo点进去查看,这里考虑到篇幅的原因,就不贴出来了
// 1. 创建configuration(配置)
// NSURLSessionConfiguration 有3个工厂方法
// default: 共享 NSHTTPCookieStorage, NSURLCache, NSURLCredentialStorage
// ephemeral: 不会存储 缓存、Cookie、证书, 适用于秘密浏览
// backgroundWithID: 可以在程序 挂起、退出、崩溃 的情况下, 上传和下载任务, ID用于向任何可能在进程外恢复后台传输的守护进程(daemon)提供上下文
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// 2. 创建sessionManager
// 2.1.创建网络请求回调queue
// 2.2.创建安全策略
// 2.3.初始化: 代理字典{task-delegate}、锁(访问代理字典的)
// 2.4.遍历session中所有的task: 数据task、上传task、下载task
// 2.4.1 为每个task创建taskDelegate, 并将代理都存入字典中
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
// 3. 根据URL创建request
NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
// 4. 创建downloadTask
// 4.1 运用NSURLSession, 根据request创建downloadTask (系统方法)
// 4.2 为downloadTask添加taskDelegate
// 4.2.1 根据downloadTask创建taskDelegate
// 4.2.2 为taskDelegate设置: manager、competion回调
// 4.2.3 为taskDelegate设置finish回调
// 4.2.4 将taskDelegate存入字典中 (加锁)
// 接收task的暂停和恢复通知 (通过替换系统的`resume`和`suspend`方法, 添加的notify实现)
// 4.2.5 为taskDelegate设置: progress回调
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request
progress:nil
destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
// 返回刚下载的文件路径
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(@"File downloaded to: %@", filePath);
}];
// 5. 开始执行
[downloadTask resume];
// 1. 创建configuration(配置)
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// 2. 创建sessionManager
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
// 3. 根据URL创建request
NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
// 4. 获取需要上传文件的URL
NSString *imageFile = [[NSBundle mainBundle] pathForResource:@"photo" ofType:@"PNG"];
NSURL *filePath = [NSURL fileURLWithPath:imageFile];
// 5. 创建uploadTask
// 5.1 运用NSURLSession根据request和fileURL创建uploadTask (系统方法)
// 5.2 为uploadTask添加taskDelegate (详情同 downloadTask 4.2)
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request
fromFile:filePath
progress:nil
completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"Success: %@ %@", response, responseObject);
}
}];
// 6. 开始执行
[uploadTask resume];
// 1. 根据method、URL、parameters、可变body block、error, 创建mutableRequest
// 1.1 根据method、URL、parameters、error, 创建mutableRequest
// 1.2 根据mutableRequest创建formData
// 1.3 parameters -> [AFQueryStringPair], 并将其添加在formData后面, 有block则调用并传回formData
// 1.4 设置request
// 1.4.1 设置bodyStream的初始和结尾边界
// 1.4.2 将bodyStream作为请求报文体
// 1.4.3 设置请求头的Content-Type和Content-Length字段
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer]
multipartFormRequestWithMethod:@"POST" // 不支持GET, HEAD类型
URLString:@"http://example.com/upload"
parameters:nil
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// formData支持三种格式的数据: NSData, FileURL, NSInputStream
NSString *imageFile = [[NSBundle mainBundle] pathForResource:@"beautiful" ofType:@"jpg"];
[formData appendPartWithFileURL:[NSURL fileURLWithPath:imageFile]
name:@"file"
fileName:@"filename.jpg"
mimeType:@"image/jpeg"
error:nil];
} error:nil];
// 2. (同downloadTask的1-2)
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
// 3. 运用AFURLSessionManager根据request, 创建streamed uploadTask
// 详情(同upload 5)
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:^(NSProgress * _Nonnull uploadProgress) {
// 子线程回调, 更新UI需要dispatch到主线程
dispatch_async(dispatch_get_main_queue(), ^{
// refresh UI
});
} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
}];
// 4. 开始执行
[uploadTask resume];
// 1. (同downloadTask的1-2)
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
// 2. 根据URL创建request
NSURL *URL = [NSURL URLWithString:@"http://httpbin.org/get"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
// 3. 运用AFURLSessionManager根据request, 创建dataTask
// 3.1 运用NSURLSession根据request, 创建dataTask (系统方法)
// 3.2 为dataTask添加taskDelegate (详情同 downloadTask 4.2)
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
}];
[dataTask resume];
AFNetworkReachabilityStatusForFlags
方法AFPostReachabilityStatusChange
方法dispatch_async(dispatch_get_main_queue(), ^{ //mo: 在主线程队列中异步执行
// 发出notification
});
__Require_noErr_Quiet
的使用, 出错跳转到 _out 和 忽略弃用警告宏的使用,详情见AFPublicKeyForCertificate
方法//mo: __Require_noErr_Quiet: 如果出错, 则跳转到 _out
/* 根据证书和政策创建一个信任管理对象
certificates: 要认证的证书+你认为对证书有用的任何其他证书
policies: 参考评估政策
trust: 返回时, 指向新创建的信任管理对象
*/
__Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
//mo: 忽略弃用警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
//mo: 评估指定 证书 和 策略 的信任 (同步的)
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
#pragma clang diagnostic pop
//mo: 在`叶证书`求值后返回其公钥
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
_out:
mutableTaskDelegatesKeyedByTaskIdentifier
字典的读写:#pragma mark - mo: 代理字典存取
//mo: 字典的操作不是线程安全的, 所以用`NSLock`加锁
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock]; //mo: 读取加锁
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate forTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock]; //mo: 写入加锁
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
NSStringFromSelector
生成的 (如: 观察 / 序列化),例如:AFHTTPRequestSerializerObservedKeyPaths
获取需要观察的属性字符串
NSMutableURLRequest
时设置
2.当某个属性的getter方法使用其他属性
的值计算返回值时, 重写keyPathsForValuesAffectingValueForKey:
方法, 返回其他属性的集合,详情见AFHTTPRequestSerializer
初始化方法
keyEnumerator
/objectEnumerator
/reverseObjectEnumerator
(不可更改的)
//mo: 用`keyEnumerator`遍历keys, 此时不可更改字典 (还有个objectEnumerator可以遍历value)
for (NSString *headerField in headers.keyEnumerator) {
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
}
AFHTTPRequestSerializer
对属性mutableHTTPRequestHeaders
的读写:// 请求头修改队列:并行
self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
#pragma mark - request headers 读写
// mo: 并行同步: 读 / 写(barrier: 保证队列之前的任务都执行完毕, 之后得等自己执行完毕)
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field {
dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
});
}
- (NSString *)valueForHTTPHeaderField:(NSString *)field {
NSString __block *value;
dispatch_sync(self.requestHeaderModificationQueue, ^{
value = [self.mutableHTTPRequestHeaders valueForKey:field];
});
return value;
}
NS_UNAVAILABLE
禁用自带初始化函数
NS_DESIGNATED_INITIALIZER
指定初始化函数
AFURLSessionManager
的respondsToSelector:
方法将判断方法是否实现,改为判断相应的block是否为空,然后在代理方法里调用响应的block。
NSSecureCoding
而不是NSCoding
解码方法是: decodeObjectOfClass:
而不是decodeObjectForKey:
因为序列化后的数据可能被篡改, 若不指定Class, decode出来的可能不是原来的对象, 有潜在风险
AFHTTPRequestSerializer
的初始化方法:
Content-Type
:请求参数类型
Accept-Language
:根据[NSLocale preferredLanguages]
方法读取本地语言,告诉服务端自己能接受的语言。
User-Agent
:app的boundId/ID/版本, 设备型号/系统/尺寸 等
Authorization
:提供 Basic Auth 认证接口,帮我们把用户名密码做 base64 编码后放入 HTTP 请求头。
一般我们请求都会按 key=value 的方式带上各种参数,
GET 方法参数直接拼在 URL 后面,POST 方法放在 body 上,
NSURLRequest
没有封装好这个参数的序列化,只能我们自己拼好字符串。
AFHTTPRequestSerializer
提供了接口,让参数可以是NSDictionary
、NSArray
、NSSet
这些类型,再由内部解析成字符串后赋给NSURLRequest
_AFURLSessionTaskSwizzling
在+load
将NSURLSessionTask
的-resume
和-suspend
替换成自己的,主要是为了添加自己的通知
semaphore_t
/semaphore_signal
/semaphore_wait
的使用
如: 用session的getTask回调获取task时, 运用了semaphore
等待block完成后才return,详情见AFURLSessionManager
的- (NSArray *)tasksForKeyPath:
方法
等等,等等。。。 看了一遍,先做一下笔记,以后回顾知新了再更新~ 小女子献丑了,文章有哪里不对的,望各位看官指正~
参考文章如下: AFNetworking(v3.1.0) 源码解析 为何需要使用HTTPs AFNetworking到底做了什么? 写的非常详细