AFNetworking学习

首先在github上面fork了AFNetworking项目,然后同步到本地github客户端,之后在本地用Xcode打开该项目。选择其中的iOS Example进行编译运行。

对于这个Example,我们并不需要使用它,而是想看看这个Sample具体干了什么,怎么使用的AFNetworking框架。所以首先要做的就是先将他跑起来,看看是一个什么样的效果。

看到了如上图这样的一个运行效果以后,现在就开始阅读Sample的代码。

项目跑起来以后,先从代码的执行顺序开始阅读源代码。iOS的项目一开始是从main开始->AppDelegate,这里面的application: didFinish里面有这样的一段代码:

- (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(__unused NSDictionary *)launchOptions
{
    NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil];
    [NSURLCache setSharedURLCache:URLCache];

    [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];

    UITableViewController *viewController = [[GlobalTimelineViewController alloc] initWithStyle:UITableViewStylePlain];
    self.navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
    self.navigationController.navigationBar.tintColor = [UIColor darkGrayColor];

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    self.window.rootViewController = self.navigationController;
    [self.window makeKeyAndVisible];

    return YES;
}

所以接下来应该进入GlobalTimelineViewController查看他的初始化代码,其他的一些操作都是附加的,并不属于主流程的内容。

- (void)viewDidLoad {
    [super viewDidLoad];

    self.title = NSLocalizedString(@"AFNetworking", nil);

    self.refreshControl = [[UIRefreshControl alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.tableView.frame.size.width, 100.0f)];
    [self.refreshControl addTarget:self action:@selector(reload:) forControlEvents:UIControlEventValueChanged];
    [self.tableView.tableHeaderView addSubview:self.refreshControl];

    self.tableView.rowHeight = 70.0f;

    [self reload:nil];
}

按照这段代码可以知道他的刷新控件的高度大小,以及在哪个函数里面加载数据。而我的目的就是看AFNetworking如何使用的,所以其他代码就不用关心了,直接进入reload这个函数。

- (void)reload:(__unused id)sender {
    self.navigationItem.rightBarButtonItem.enabled = NO;

    NSURLSessionTask *task = [Post globalTimelinePostsWithBlock:^(NSArray *posts, NSError *error) {
        if (!error) {
            self.posts = posts;
            [self.tableView reloadData];
        }
    }];

    [self.refreshControl setRefreshingWithStateOfTask:task];
}

从字面理解这段代码是说创建了一个NSURLSessionTask对象(创建了一个任务),然后refreshControl根据task的执行情况设置状态。(一般而言写的规范的项目通过函数命名是可以直接了解这个函数作用的),按照我们的目的,现在需要进入globalTimelinePostsWithBlock:函数

+ (NSURLSessionDataTask *)globalTimelinePostsWithBlock:(void (^)(NSArray *posts, NSError *error))block
{
    return [[AFAppDotNetAPIClient sharedClient] GET:@"stream/0/posts/stream/global" parameters:nil progress:nil success:^(NSURLSessionDataTask * __unused task, id JSON) {
        NSArray *postsFromResponse = [JSON valueForKeyPath:@"data"];
        NSMutableArray *mutablePosts = [NSMutableArray arrayWithCapacity:[postsFromResponse count]];
        for (NSDictionary *attributes in postsFromResponse) {
            Post *post = [[Post alloc] initWithAttributes:attributes];
            [mutablePosts addObject:post];
        }

        if (block) {
            block([NSArray arrayWithArray:mutablePosts], nil);
        }
    } failure:^(NSURLSessionDataTask *__unused task, NSError *error) {
        if (block) {
            block([NSArray array], error);
        }
    }];
}

这个函数其实就是调用GET:parameters: progress:success:failure函数返回一个session的dataTask,如果返回成功,并且block这个参数不为空,则执行传递过来的block这个匿名函数。所以接下来查看这个GET:parameters: progress:success: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
{

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

    [dataTask resume];

    return dataTask;
}

这个函数调用 dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:函数返回一个dataTask任务,并且调用resume启动这个任务。所以再进入这个函数:

- (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;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }

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

这个函数内部多了一些变量与参数,但是这个我们并不需要关心,我们只需要弄懂他的主要逻辑是什么就可以了,这个函数主要是创建了一个request然后利用这个request创建了一个任务。所以接下来就可以省略request创建的那个函数,直接进入主逻辑的dataTaskWithRequest这个函数:

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

可以看出这个函数的内部主逻辑是安全的创建一个dataTask根据request,然后为这个dataTask设置一个代理。dataTaskWithRequest:函数是NSURLSession中的一个方法,所以进入addDelegateForDataTask:函数

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

这个函数的主逻辑是创建一个AFURLSessioinManagerTaskDelegate类型的对象,然后设置好这个对象的内部属性,以及为这个dataTask设置代理,因此继续进入setDelegate这个函数

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

这个函数的主要逻辑就是加锁之后将这个delegate添加入数组之中,然后为任务设置好观察者和创建相应的处理方式(setupProgressFortask)。到此我们基本上已经知道如何使用GET:parameters: progress:success:failure以及其内部的处理主逻辑。

现在还有一个问题,关于网络请求创建了一个session的dataTask,那么如何获得回应,数据是从哪里回来的。获得了回应以后AFNetworking又是在哪里将NSData(二进制数据)转成我们需要的JSON格式数据的(假定设置的格式为JSON)。

稍微对iOS中的网络部分有些了解就可以知道:iOS使用delegate来设置回调函数(即数据从服务端传回以后调用的函数),具体关于NSURLSession的知识请参考以下两个网址:

http://swiftcafe.io/2015/12/20/nsurlsession/

http://code.tutsplus.com/tutorials/networking-with-nsurlsession-part-1--mobile-21394

因此可以很明显的知道NSURLSessionDataDelegate内部就是回调函数,但是这个session是在哪里设置的这个代理呢,如果不太清楚NSURLSession的整体处理方式,可以直接用matching的搜索方式搜索delegate,然后可以发现

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }

    self.sessionConfiguration = configuration;

    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;

    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    self.responseSerializer = [AFJSONResponseSerializer serializer];

    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif

    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;

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

这个函数里面的sessionWithConfiguration:delegate: delegateQueue函数是最符合我们要求的设置这个session代理的位置,那么这个代理最终会在我们项目中的哪个函数设置呢,我们依旧可以采用搜索的方式搜索看项目中哪里调用了这个initWithSessionConfiguration

但是最好还是在这个地方设置一个断点。从左边的函数调用栈来看最终是在哪个函数调用的:[AFAppDotNetAPIClient sharedClient]

找到了设置代理的位置,那么我们再利用这个Xcode提供的函数调用栈看由网络传递回来的NSData是在哪里做了处理变成了JSON格式。首先在收到数据并进行处理的GET:parameters:progress:success:failure:这个API的success中设置断点。

可以看出需要追溯到URLSession:task:didCompleteWithError代理方法。

- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    __strong AFURLSessionManager *manager = self.manager;

    __block id responseObject = nil;

    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;

    //Performance Improvement from #2672
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];
        //We no longer need the reference, so nil it out to gain back some memory.
        self.mutableData = nil;
    }

    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;

        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }

            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }

            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }

            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }

                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
}

这个函数里面调用了这样一个responseObject = [manager.responseSerializerresponseObjectForResponse:task.response data:dataerror:&serializationError];函数,转入这个函数查看里面有一个responseObject = [NSJSONSerializationJSONObjectWithData:data options:self.readingOptionserror:&serializationError];这个函数就是将普通的二进制数转成JSON格式的内置方法。

时间: 2024-10-27 14:17:12

AFNetworking学习的相关文章

iOS 8:【转】AFNetworking 学习笔记二

源地址:http://fann.im/blog/2013/04/29/afnetworking-notes-2/ AFNetworking 学习笔记 的后续,记录一些 AFN 比较隐蔽的知识点. AFN 的设计过于理想化 AFN 的架构设计非常棒,使用起来也很简单,但一些设计过于理想化,在实际开发中会有一些条件不能满足,这时候 AFN 就会出现一些“坑”. 1. 缓存策略 NSURLRequest 默认的缓存策略是 NSURLRequestUseProtocolCachePolicy,网络请求是

iOS 8:【转】AFNetworking 学习笔记

源地址:http://fann.im/blog/2012/08/21/afnetworking-notes/ 这篇笔记是在 AFN v0.10.1 时候写的,AFN v1.0 以后加入了不少新东西,比如 SSL 支持,不过整体结构没有变化. 后续跟进了一篇 AFNetworking Notes 2 上图来自 @mattt 对 AFN 的介绍:Everybody Loves AFNetworking And So Can You!. 学习 AFN,简单记录一下以加深自己理解. AFN 的基础部分是

AFNetworking 学习笔记

从3.0开始 一转眼,AF已经更新到了3.0版本.目前cocoapods上的最新版本是3.0 beta1.在3.0的版本里面,AF全面地使用NSURLSession代替了NSURLConnection.之前花了一些时间学习NSURLSession,在这里的学习终于派上了用场.在这里主要学习3.0版本的使用.希望在项目中能够顺利地过度到AFNetwoking 3.0版本.此外,随着Objective-c慢慢被Swift替代,AFNetworking 3.0可能是最后一个大版本更新.本文会一直随着A

AFNetworking自我总结

AFNetworking 简介 目前国内开发网络应用使用最多的第三方框架 是专为 Mac OS & iOS 设计的一套网络框架 对 NSURLConnection 和 NSURLSession 做了封装 提供有丰富的 API 提供了完善的错误解决方案 使用简单 官网地址 https://github.com/AFNetworking/AFNetworking 学习第三方框架的步骤 获取框架 $ git clone https://github.com/AFNetworking/AFNetwork

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

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

GET/POST请求的使用《极客学院 --AFNetworking 2.x 网络解析详解--2》学习笔记

AFNetworking是开源代码排名第一的开源库. GET请求的请求正文 一般都是明文显示的,携带的数据量小. POST用于处理复杂的业务,并不用明文的请求,其实POST请求可以携带更多的参数,只是不明文显示,所以我们在有的时候,有的网址你是看不到GET请求的网址有?已经?后面携带的明文请求数据. 抓包工具:网址:http://www.cnblogs.com/YouXianMing/p/4544824.html 测试的网址: // http://webservice.webxml.com.cn

AFNetWorking的学习

1    类库功能简介 1.1  AFNetworking的大体思路 1.1.1 NSURLConnection + NSOperation daozhe NSURLConnection 是 Foundation URL加载系统的基石.一个 NSURLConnection 异步地加载一个 NSURLRequest 对象,调用delegate 的 NSURLResponse / NSHTTPURLResponse 方法,其 NSData 被发送到服务器或从服务器读取:delegate还可用来处理 

iOS进阶学习-初识AFNetworking

一.AFNetworking简介 AFNetworking是一个 在iOS开发中 使用非常多网络开源库 适用于iOS以及Mac OS X.它构建于在(Apple iOS开发文档) NSURLConnection ,  NSOperation , 以及其他熟悉的Foundation技术之上. 它拥有良好的架构,丰富的api,以及模块化构建方式,使得使用起来非常轻松. 官方链接http://cocoadocs.org/docsets/AFNetworking/1.3.0/. AFNetworking

iOS 新人学习AFNetworking(一)

源代码下载:http://download.csdn.net/detail/haogaoming123/8555077 随着asihttprequest的停止更新,许多人都转向了AFNetworking. MKNetworkKit.我也是其中一个.于是我从网上找了许多文章作参考,但是结果都是失败告终.研究了好久都搞不透,最后还是请人帮忙搞定了.经常从网上索取免费资料的一员,要有回报的思想,也为了让更多的人少走些弯路,所以下面是代码:(有错误可以指出) 首先:将AFNetworking.UIKit