一、manager初始化
-
manager
是类方法,工厂设计模式,不是单例哦 - 线程:2.0 AF 建立在常驻线程,3.0 在Runloop NSOperationQueue
NSString *urlStr = @"https://api.douban.com/v2/movie/top250";
NSDictionary *dic = @{@"start":@(1),
@"count":@(5)
};
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:urlStr parameters:dic progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
- 1、点击manager
+ (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];
}
- 2.1、调用父类的方法
- 2.2、给url添加“/”。重定向,再请求一次,浪费带宽;表示目录,信息拼接,路由。
SEO(Search Engine Optimization):
汉译为搜索引擎优化。 - 2.3、给requestSerializer、responseSerializer设置默认值
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
//调用父类初始化方法
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
/*
为了确保NSURL +URLWithString:relativeToURL: works可以正确执行,在baseurlpath的最后添加‘/’
*/
//url有值且没有‘/’,那么在url的末尾添加‘/’
// 解释1: 防止重定向 -- www.baidu.com 调用2次 浪费带宽
// 解释2: 表示目录 信息的拼接 路由
// 解释3: SEO 搜索引擎优化
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;
}
- 3.1、初始化一个session
- 3.2、给manager的属性设置初始值
- (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。其实讲道理来说在初始化中调用这个方法应该里面一个task都不会有
//后台任务重新回来初始化session,可能就会有先前的任务
//https://github.com/AFNetworking/AFNetworking/issues/3499
[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;
}
二、request方法封装
- request : 请求行 + 请求头 + 请求体
- 请求头
- 请求头参数
- (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;
}
- 点击
dataTaskWithHTTPMethod...
//1.生成request,2.通过request生成task
- (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;
}
- 点击
requestWithMethod...
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
//断言如果nil,直接打印出来
NSParameterAssert(method);
NSParameterAssert(URLString);
//我们传进来的是一个字符串,在这里它帮你转成url
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
//设置请求方式(get、post、put。。。)
mutableRequest.HTTPMethod = method;
//将request的各种属性遍历,给NSMutableURLRequest自带的属性赋值
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
//给设置过得的属性,添加到request(如:timeout)
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
//通过kvc动态的给mutableRequest添加value
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
//将传入的参数进行编码,拼接到url后并返回 coount=5&start=1
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
NSLog(@"request'''''''''%@",mutableRequest);
return mutableRequest;
}
- 点击
requestBySerializingRequest...
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
//给mutableRequest.headfiled赋值
/*
1.请求行(状态行):get,url,http协议1.1
2.请求头:conttent-type,accept-language
3.请求体:get/post get参数拼接在url后面 post数据放在body
*/
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
//将我们传入的字典转成字符串
NSString *query = nil;
if (parameters) {
//自定义的解析方式
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
//默认解析方式,dic- count=5&start=1
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
//将parameters传入这个c函数
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
//count=5&start=1
NSLog(@"query:%@",query);
//最后判断该request中是否包含了GET、HEAD、DELETE(都包含在HTTPMethodsEncodingParametersInURI)。因为这几个method的query是拼接到url后面的。而POST、PUT是把query拼接到http body中的。
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @"";
}
//函数会判断request的Content-Type是否设置了,如果没有,就默认设置为application/x-www-form-urlencoded
//application/x-www-form-urlencoded是常用的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
//设置请求体
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
三、task与代理(AFURLSessionManagerTaskDelegate)的关系
- manager -> session -> task -> delegate -> /weak/ -> manager
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
存在了mutableTaskDelegatesKeyedByTaskIdentifier字典中,task.taskIdentifier为key,delegate为value
四、manager与代理(AFURLSessionManagerTaskDelegate)的关系
AFURLSessionManagerTaskDelegate类 中 有AFURLSessionManager属性
@property (nonatomic, weak) AFURLSessionManager *manager;
网友评论