首先在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格式的内置方法。