AFNetworking网络请求源码精讲(一) -- GET操作

AFNetworking

AFNetworking是一款优秀的网络请求框架,从这篇文章开始,我们一起来看关于AFNetworking常用的源码解析。通常,在网络请求中,我们会进行GET和POST操作,面对这种方式,系统提供了NSURL和NSURLRequest来实现GET和POST请求。

系统方式:

	NSURL *url = [[NSURL alloc] initWithString:@"https://www.baidu.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
        NSLog(@"%@",dict);
    }];
    [task resume];

AFNetworking方式:

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:(nonnull NSString *)
      parameters:(nullable id)
         headers:(nullable NSDictionary<NSString *,NSString *> *)
        progress:^(NSProgress * _Nonnull downloadProgress) {
            
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            
        }];

思考:为什么开发中常用AFNetworking,而减少了系统原装的请求方式???
通过对比两种方式,我们不难看出AFNetworking给开发者直面的优点是集中性较强,而系统方式显得更加分散,这不便于后期修改和维护操作。具体内部是有和差异,我们拭目以待。

事实上,AFNetworking将系统的方法集中在封装就形成了现在这个优秀的框架。

GET方式

AFNetworking 提供了三种GET方式,但是有两种已经作废,我们来看一下,如果调用作废的它会怎么做。

- (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(id)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure{
	return [self GET:URLString parameters:parameters headers:nil progress:nil success:success failure:failure];
}

- (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{
	return [self GET:URLString parameters:parameters headers:nil progress:downloadProgress success:success failure:failure];
}

- (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(id)parameters headers:(nullable NSDictionary <NSString *, NSString *> *)headers progress:(void (^)(NSProgress * _Nonnull))downloadProgress success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure{...}

前面两个方法已经失效,因此,即使开发者调用了该方法,也会内部直接进行调用第三个方法,第三个看起来更加完善,包含了:

  1. 传入参数
  2. 下载进度
  3. 成功回调
  4. 错误回调

既然是封装,我们看系统之前,想要实现GET方法,是启动了一个SessionTask任务,并且通过resume进行开启任务。那么,我们猜测GET方法中提供的方式,肯定是返回了SessionTask,并且,已经为用户自动开启了任务,来使得编码更加简单,我们来验证一下这样的设计。

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                      headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                          headers:headers
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
    [dataTask resume];
    
    return dataTask;
}

的确是这样,又一次对NSURLSessionDataTask进行了封装。我们进入源码来分析一下,是如何封装的。

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                         headers:(NSDictionary <NSString *, NSString *> *)headers
                                  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;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    
    /* for-in循环遍历headers数组的key值 */
    for (NSString *headerField in headers.keyEnumerator) {
        /**
         *  request添加请求头
         *  headers[headerField] -- 取出value值
         */
        [request addValue:headers[headerField] forHTTPHeaderField:headerField];
    }
    
    /* 如果进入了循环serializationError,则说明出现了错误  */
    if (serializationError) {
        if (failure) {
            /* 如果出现了错误,则开辟一个线程,用来处理错误问题 */
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }

        return nil;
    }

    /* __block参数表明是在block中使用的变量 */
    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
                           /* dataTask在外部声明了为block类型 */
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];

    /* 返回最终的数据任务 */
    return dataTask;
}

这一块代码我们分板块来分析。
首先,要生成NSURLSessionDataTask,需要一个Request,这里面AFNetworking采用了NSMutableURLRequest来生成NSURLSessionDataTask。

NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];

这里有一个self.requestSerializer,请求序列化,这个东西我们需要进一步的研究。

/**
Requests created with requestWithMethod:URLString:parameters: & multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock: are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of AFHTTPRequestSerializer, which serializes query string parameters for GET, HEAD, and DELETE requests, or otherwise URL-form-encodes HTTP message bodies.
@warning requestSerializer must not be nil.
*/
@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer;

这个requestSerializer是通过协议来创建的。

默认情况下,此属性设置为“ AFJSONResponseSerializer”的实例。

看到这里,你肯定知道这或许又是一次封装,我们先跳过,看后面又做了什么操作。

第二步,通过forin循环遍历开发者传入的请求头,取出对应的value和key,保存在request中。

 for (NSString *headerField in headers.keyEnumerator) {
     [request addValue:headers[headerField] forHTTPHeaderField:headerField];
 }  

第三步,利用request生成一个dataTask并返回,这里对于dataTask又做了一次封装。

__block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
                           /* dataTask在外部声明了为block类型 */
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];
return dataTask;

第四步,分析封装的dataTask,此时传入的request是包含着key和value的。

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                               uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                             downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler {

    __block NSURLSessionDataTask *dataTask = nil;
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
     dataTask = [self.session dataTaskWithRequest:request];
 });

这一部分,根据request创建了一个dataTask。

注意:如果要修改Block以外变量,应该将该变量设置为__block类型,从而可以在Block中进行赋值修改操作

url_session_manager_create_task_safely传入的是一个Block

dispatch_sync(url_session_manager_creation_queue(), block);

其核心做的是,同步线程阻塞来创建dataTask。

思考:为什么要用同步阻塞线程,难道异步操作不可以吗???
AFNetworking之所以设置同步操作,是因为网络访问不是一次性的,一次GET请求是多次响应的过程,我们如果不用阻塞线程,就会使得创建的任务出现严重的错乱问题,所以,这里不能使用多线程的异步操作,而应该使用同步操作,来保证创建的dataTask是正确的。

剩下的操作,AFNetworking就交给了协议来做,我们先到此为止。
刚才还遗留一个问题,requestSerializer具体如何实现的。

requestSerializer
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];

    /* 如果url位false则插入断言 */
    NSParameterAssert(url);

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    
    /* 设置HTTP访问的方法 */
    mutableRequest.HTTPMethod = method;

    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

	return mutableRequest;
}

其中内部通过url创建一个mutableURLRequest。

NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];

HTTPMethod设置了访问方式,这样就可以重复利用在GET和POST更多的访问方式上。

mutableRequest.HTTPMethod = method;

剩下部分就是进行对Request进行了设置。

至此,基本上对GET请求了有了一定的认识,对于更为具体详细的是如何设置Request,AFNetworking如何进行代理操作的,将在下一篇POST请求进一步分析。

源码下载

AFNetworking中文源码下载: GitHub.

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值