AFNetworking源码阅读

get方法:

- (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;

方法名:GET

URLString: 请求的url地址,必须加上协议(如:http://localhost:8080/ios)

parameters: url中的参数,以NSDictionNarry的方式存储

progress: 在下载的过程中,会持续调用这个block,这个block是在session queue中被调用的,而不是在main queue中。

success: 下载成功后,调用这个block

failure: 下载失败后,调用这个block

先了解下foundation框架中的网络请求NSURLSessionDataTask的最基本使用:

//get请求
- (void)getRequest {

    //得到session对象
    NSURLSession *session = [NSURLSession sharedSession];

    NSURL *url = [NSURL URLWithString:@"http://10.81.160.87:8080/ios?name=dj&sex=male"];

    //创建一个任务
    NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error) {
            NSLog(@"error: %@",error);
        } else {
            // 需要对data进行转码
            NSLog(@"%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);

            NSLog(@"data:%@\nlength:%ld", data, data.length);
        }
    }];

    //开始任务
    [task resume];
}

//post请求
- (void)postRequest {

    //得到session对象
    NSURLSession *session = [NSURLSession sharedSession];

    //设置url地址
    NSURL *url = [NSURL URLWithString:@"http://10.81.160.87:8080/ios"];

    //设置request请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    //设置request请求的方法
    request.HTTPMethod = @"post";

    //设置request请求的参数
    request.HTTPBody = [@"name=dj&&sex=male" dataUsingEncoding:NSUTF8StringEncoding];

    //创建一个任务
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error) {
            NSLog(@"error: %@",error);
        } else {
            // 需要对data进行转码
            NSLog(@"%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);

            NSLog(@"data:%@\nlength:%ld", data, data.length);
        }
    }];

    //开始工作
    [task resume];
}

通过协议的实现,我们可以实现更多的功能,如下载上传时的状态

知道了NSURLSessionDataTask的基本应用,接着看

NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];

这个方法得到一个NSURLSessionDataTask对象dataTask,如何得到,进入dataTaskWithHTTPMethod方法

首先是一个得到NSMutableURLRequest的方法

NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method); //不存在就抛出异常,断言
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];  //将url字符串转为url

    NSParameterAssert(url);

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];  //初始化一个NSMutableURLRequest
    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;
}

在上面有个AFHTTPRequestSerializerObservedKeyPaths方法,这个方法返回的是一个字符串数组,而且是一个单例对象

static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
    static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
    });

    return _AFHTTPRequestSerializerObservedKeyPaths;
}

这里的设置其实用到了Kvo

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #4f8187 }
p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #31595d }
p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
p.p4 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { color: #ba2da2 }
span.s2 { color: #000000 }
span.s3 { }
span.s4 { color: #703daa }
span.s5 { color: #3e1e81 }
span.s6 { color: #4f8187 }

//在初始化处  

self.mutableObservedChangedKeyPaths = [NSMutableSet set];

for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {

if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {

[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];

}

}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(__unused id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (context == AFHTTPRequestSerializerObserverContext) {
        if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
            [self.mutableObservedChangedKeyPaths removeObject:keyPath];
        } else {
            [self.mutableObservedChangedKeyPaths addObject:keyPath];
        }
    }
}

当keyPath发生改变,mutableObservedChangedKeyPaths这个set容器就添加一个keyPath

在刚刚[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];中,就是将发生改变的keyPath加入到mutableRequest中。

那keyPath是什么呢?

我们其实可以先看下,mutableRequest里面都有哪些属性可以设置

@property (nullable, copy) NSURL *URL;
@property NSURLRequestCachePolicy cachePolicy;
@property NSTimeInterval timeoutInterval;
@property (nullable, copy) NSURL *mainDocumentURL;
@property NSURLRequestNetworkServiceType networkServiceType NS_AVAILABLE(10_7, 4_0);
@property BOOL allowsCellularAccess NS_AVAILABLE(10_8, 6_0);

然后我们再看看requestSerializer这个对象,因为我们刚刚requestWithMethod方法就是它的实例方法,我们在看看这个requestWithMethod这个方法中的属性,

这里直接复制源码,刚好可以解释mutableRequest中各个属性的作用和初始值

/**
 The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default.
 */
@property (nonatomic, assign) NSStringEncoding stringEncoding;

/**
 Whether created requests can use the device’s cellular radio (if present). `YES` by default.

 @see NSMutableURLRequest -setAllowsCellularAccess:
 */
@property (nonatomic, assign) BOOL allowsCellularAccess;

/**
 The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default.

 @see NSMutableURLRequest -setCachePolicy:
 */
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;

/**
 Whether created requests should use the default cookie handling. `YES` by default.

 @see NSMutableURLRequest -setHTTPShouldHandleCookies:
 */
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;

/**
 Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default

 @see NSMutableURLRequest -setHTTPShouldUsePipelining:
 */
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;

/**
 The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default.

 @see NSMutableURLRequest -setNetworkServiceType:
 */
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;

/**
 The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds.

 @see NSMutableURLRequest -setTimeoutInterval:
 */
@property (nonatomic, assign) NSTimeInterval timeoutInterval;

现在就清晰了,我们通过设置requestSerializer这个对象的属性,kvo检测到属性的变化,将变化的属性放入mutableObservedChangedKeyPaths这个set容器中,

然后在将set容器中的值,设置到mutableRequest中,这样我们就改变了mutableRequest的属性

接下来还有一个方法:

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

作用是将参数加到mutableRequest中,分析下它的实现

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    NSMutableURLRequest *mutableRequest = [request mutableCopy];  //设置头请求
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    NSString *query = nil;  //有参数
    if (parameters) {    //如果你实现了下面queryStringSerialization这个块,那你的query就是你自己实现的
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);
        //如有出错,返回nil
            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                return nil;
            }
        } else {       //这是个枚举类型的属性,默认为AFHTTPRequestQueryStringDefaultStyle       //AFQueryStringFromParameters将字典转为带&的字符串
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }
   //HTTPMethodsEncodingParametersInURI是个set容器,初始数据有get,head,deal  //判断是否是get请求
    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {     //封装url
        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 = @"";
        }    //添加头请求
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        }    //设置body参数
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }

    return mutableRequest;
}

在上面设置头请求的时候,其实要知道,它是通过mutableHTTPRequestHeaders得到的

- (NSDictionary *)HTTPRequestHeaders {
    return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
}

而这个mutableHTTPRequestHeaders需要通过下面这个方法进行设置==

- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
    [self.mutableHTTPRequestHeaders setValue:value forKey:field];
}

知道了参数的设置,就这样我们得到了这个NSMutableURLRequest对象

然后判断是否有错误,若有,则抛出错误,并返回nil

接下来执行

 __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;

dataTaskWithRequest方法得到一个NSURLSessionDataTask对象,并返回这个对象

看下实现

- (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;
}

看下静态方法url_session_manager_create_task_safely的实现

static void url_session_manager_create_task_safely(dispatch_block_t block) {
    if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
        // Fix of bug
        // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
        // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
       //同步执行串行队列     dispatch_sync(url_session_manager_creation_queue(), block);
    } else {
        block();
    }
}

url_session_manager_creation_queue()静态方法的实现

//单例模式,返回一个串行队列static dispatch_queue_t url_session_manager_creation_queue() {
    static dispatch_queue_t af_url_session_manager_creation_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
    });

    return af_url_session_manager_creation_queue;
}

AFURLSessionManagerTaskDelegate这个类遵从了NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate三个协议。

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

通过上面的addDelegateForDataTask方法,AFURLSessionManagerTaskDelegate得到了各个属性,当在NSURLSessionDataTask开始工作时,会通过协议的实现调用这些属性块。

到这里,就可以通过[dataTask resume];来开始http的get请求了

其实分析下来可以发现,无论是get请求,还是post请求,无论下载,还是上传,都是

[self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
[self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];

其实就改变了method参数,当然上传通过post,下载通过get

AFNetworking的源码分析当然远远不止这些,我只是记录下我暂时使用到方法在框架中是如何实现,如何封装NSURLSessionDataTask的

时间: 2024-08-06 08:00:38

AFNetworking源码阅读的相关文章

【原】AFNetworking源码阅读(六)

[原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AFNetworking的网络安全策略,尤其指HTTPS(大家可以先简单了解下HTTPS).再一个就是分析下AFNetworkReachabilityManager文件,看看AFNetworking如何解决网络状态的检测. 2. AFSecurityPolicy - 网络安全策略 之前我们在AFURLS

【原】AFNetworking源码阅读(四)

[原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDelegate类所实现的NSURLSession相关的代理方法,甚至连dataTask.uploadTask.downloadTask这几个基本概念也没说.这一篇就是为了集中消灭这些遗留问题. 2. AFURLSessionManagerTaskDelegate的代理方法 此处实现的仍然是NSURLS

【原】AFNetworking源码阅读(三)

[原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data task.但是对于NSURLSession部分却没有提及.主要是精力有限,准备在这一部分把NSURLSession的知识好好梳理一遍.一切先从上一篇中的addDelegateForDataTask:函数说起,然后再介绍AFURLSessionManagerTaskDelegate,最后结合AFURLSes

【学习总结】AFNetworking源码阅读(一)

1. 前言 2. iOS Example代码结构 3.AFNetworkActivityIndicatorManager 4. UIRefreshControl+AFNetworking 5. AFNetworkActivityManagerTests+AFUIRefreshControlTests 6. 参考文章 回到顶部 1. 前言 AFNetworking版本:3.0.4 静下心来阅读一下AFNetworking源代码,我想回到最原点,从AFNetworking提供的iOS Example

AFNetworking源码阅读(二)AFNetworkReachabilityManager

AFNetworkReachabilityManager.h 1 #if !TARGET_OS_WATCH 2 ... 3 #endif 表示当前只适用于 非WATCH 平台开发. 1 #import <SystemConfiguration/SystemConfiguration.h>

AFNetworking 3.0源码阅读 - AFURLSessionManager

这次来说一下AFURLSessionManager 从头文件的英文注释可以看出AFURLSessionManager类创建并管理着NSURLSession对象,而NSURLSession又是基于NSURLSessionConfiguration的.同时该类也是AFHTTPSessionManager的父类,下一篇来讲. AFURLSessionManager实现了四个协议 1.NSURLSessionDelegate URLSession:didBecomeInvalidWithError: U

【原】FMDB源码阅读(一)

[原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于FMDB组件我是一点都没用过.好在FMDB源码中的main.m文件提供了大量的示例,况且网上也有很多最佳实践的例子,我就不在这献丑了.我们先从一个最简单的FMDB的例子开始: // 找到用户目录下的Documents文件夹位置 NSString* docsdir = [NSSearchPathFor

iOS AFNetWorking源码详解(一)

来源:Yuzeyang 链接:http://zeeyang.com/2016/02/21/AFNetWorking-one/ 首先来介绍下AFNetWorking,官方介绍如下: AFNetworking is a delightful networking library for iOS and Mac OS X. It’s built on top of theFoundation URL Loading System, extending the powerful high-level n

CI框架源码阅读笔记3 全局函数Common.php

从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作). 打开Common.php中,第一行代码就非常诡异: if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 上一篇(CI框架源码阅读笔记2 一切的入口 index