AFNetworking源码<一>

AFNURLConnectionOperation

AFNURLConnectionOperation是AFNetworking之前框架中完成网络请求的最主要的类, AFNURLConnectionOperation继承于NSOperation,服从NSURLConnectionDelegate,NSURLConnectionDataDelegate, NSSecureCoding,NSCopying协议。是一个封装好的请求任务单元,有请求request、响应response、加密证书securityPolicy,下传流inputStream、上传流outputStream、完成的OperationcompletionQueue,完成的groupoperationGroup等属性。

AFURLSessionManager

在Xcode7之后, NSURLConnection的API已经正式被苹果遗弃,虽然还是可以使用,但是新功能不会再添加,不会对其再进行维护。所以是时候对NSURLSession的API进行运用了。

弃用的类

下面的类已从AFNetworking 3.0中废弃:
AFURLConnectionOperation
AFHTTPRequestOperation
AFHTTPRequestOperationManager

AFURLSessionManager继承NSObject,服从NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying协议。是用来管理NSURLSession的封装单元。

AFURLSessionManager.m中包含三部分:
  1. AFURLSessionManagerTaskDelegate:用来管理网络请求的信息,并且处理请求完成回调
  2. _AFURLSessionTaskSwizzling:用来完成resume与af_resume的swizzling的类
  3. AFURLSessionManager:主体控制网络请求的相关功能

AFURLSessionManager中,创建时,创建一个NSOperationQueue,最大operation数为1,规定请求delegate的Operation是唯一的。在这个类中,开辟了唯一的一个串行队列url_session_manager_creation_queue来调用NSURLSession的请求方法,唯一的一个并行队列url_session_manager_processing_queue来进行进程,使用唯一的一个groupurl_session_manager_completion_group来管理完成线程。

为保证线程安全,所以得单例都是使用dispatch_once生成

static dispatch_queue_t url_session_manager_processing_queue() {
    static dispatch_queue_t af_url_session_manager_processing_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
    });

    return af_url_session_manager_processing_queue;
}

并且通过kvo监控resume情况

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:nil];

调用网络请求的方法中,会创建一个NSURLSessionDataTask对象,用来返回给用户,在之前单例创建的线程中url_session_manager_creation_queue调用init中创建的session属性来执行请求方法。

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionDataTask *dataTask = nil;
    dispatch_sync(url_session_manager_creation_queue(), ^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    [self addDelegateForDataTask:dataTask completionHandler:completionHandler];

    return dataTask;

- (void)addDelegateForDataTask可以看出来在框架中用于管理请求信息与数据的回调的是AFURLSessionManagerTaskDelegate

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
             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];
}

AFURLSessionManagerTaskDelegateNSURLSessionTask的关系是由AFURLSessionManager管理的。addDelegateForDataTask方法中除了创建AFURLSessionManagerTaskDelegate类与配置其属性以外,如果是上传,会配置content-length以及delegate.progress的相关block属性。如果是下载,则会配置完成download的block属性

if (destination) {
        delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
            return destination(location, task.response);
        };
    }

    if (progress) {
        *progress = delegate.progress;
    }

    downloadTask.taskDescription = self.taskDescriptionForSessionTasks;

之后的一个方法是setDelegate:forTask:,它将AFURLSessionManagerTaskDelegateNSURLSessionDataTask进行了绑定。方便之后对于请求数据的查找与回调。
mutableTaskDelegatesKeyedByTaskIdentifier是将task的taskIdentifier与delegate绑定的重要属性

self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;

我们想要得到包括进程等等属性都在delegate里面可以得到。
在resume时需要外界能接收到一个didResume的消息,所以需要调用taskDidResume方法。

- (void)taskDidResume:(NSNotification *)notification {
    NSURLSessionTask *task = notification.object;
    if ([task respondsToSelector:@selector(taskDescription)]) {
        if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
            });
        }
    }
}

终于得到了AFNetworkingTaskDidResumeNotification,有了它就方便多了,AFNetworkingTaskDidSuspendNotification也是同理。
在这里其实还有一个很重要的知识点,AFNetworking对NSURLSessionTask中的state进行了kvo处理,但是在iOS8上会导致莫名的crash,可能是由于iOS7与iOS8上的NSURLSessionTask是不同的,还好之后有大神通过swizzling解决了这个问题。在源码中我们看到如果想要得到AFNetworkingTaskDidResumeNotification通知,就要执行- (void)taskDidResume:(NSNotification *)notification方法,而这个方法是需要通过监听AFNSURLSessionTaskDidResumeNotification通知来执行的。所以我们需要对resume方法进行修改,在resume方法中添加此通知,于是就通过swizzling在+ (void)load中将af_resume与resume方法的imp进行交换。

af_resume方法实现
- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_resume];
    
    if (state != NSURLSessionTaskStateRunning) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}

对swizzling有兴趣的可以看看这个源码,这里就不延伸了,简单来说就是将af_resume的函数指针与resume的函数指针进行调换,在调用resume方法时其实调用的是af_resume,调用af_resume时则相反。
在网络请求的过程中,AFURLSessionManager在执行相关的NSURLSessionDelegate方法时,会调用这个请求相对应的AFURLSessionManagerTaskDelegate类中的对应的NSURLSessionDelegate方法,在AFURLSessionManagerTaskDelegate中完成请求的信息展示和数据回调。

AFURLSessionManager中的NSURLSessionDelegate回调
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // delegate may be nil when completing a task in the background
    if (delegate) {
        [delegate URLSession:session task:task didCompleteWithError:error];

        [self removeDelegateForTask:task];
    }

    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }

}

AFURLSessionManagerTaskDelegate中的complete方法中则创建一个userInfo字典,储存网络请求的相关信息,然后通过消息中心postAFNetworkingTaskDidCompleteNotification消息将userInfo传出。其中,还在completionGroup中调用主线程通过执行completionHandler将请求数据传出。到此为止终于完成了一次完整的网络请求。

AFURLSessionManagerTaskDelegate中的NSURLSessionDelegate回调
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
    __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];
                });
            });
        });
    }
#pragma clang diagnostic pop
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容