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中,創(chuàng)建時,創(chuàng)建一個NSOperationQueue,最大operation數為1,規(guī)定請求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監(jiān)控resume情況

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

調用網絡請求的方法中,會創(chuàng)建一個NSURLSessionDataTask對象,用來返回給用戶,在之前單例創(chuàng)建的線程中url_session_manager_creation_queue調用init中創(chuàng)建的session屬性來執(zhí)行請求方法是鬼。

- (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方法中除了創(chuàng)建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通知,就要執(zhí)行- (void)taskDidResume:(NSNotification *)notification方法,而這個方法是需要通過監(jiān)聽AFNSURLSessionTaskDidResumeNotification通知來執(zhí)行的慰安。所以我們需要對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在執(zhí)行相關的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方法中則創(chuàng)建一個userInfo字典,儲存網絡請求的相關信息,然后通過消息中心postAFNetworkingTaskDidCompleteNotification消息將userInfo傳出。其中,還在completionGroup中調用主線程通過執(zhí)行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閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谜疤,死亡現場離奇詭異佃延,居然都是意外死亡,警方通過查閱死者的電腦和手機夷磕,發(fā)現死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門履肃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坐桩,你說我怎么就攤上這事尺棋。” “怎么了绵跷?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵陡鹃,是天一觀的道長。 經常有香客問我抖坪,道長萍鲸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任擦俐,我火速辦了婚禮脊阴,結果婚禮上,老公的妹妹穿的比我還像新娘蚯瞧。我一直安慰自己嘿期,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布埋合。 她就那樣靜靜地躺著备徐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甚颂。 梳的紋絲不亂的頭發(fā)上蜜猾,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音振诬,去河邊找鬼蹭睡。 笑死,一個胖子當著我的面吹牛赶么,可吹牛的內容都是我干的肩豁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼清钥!你這毒婦竟也來了琼锋?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤祟昭,失蹤者是張志新(化名)和其女友劉穎斩例,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體从橘,經...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年础钠,在試婚紗的時候發(fā)現自己被綠了恰力。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡旗吁,死狀恐怖踩萎,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情很钓,我是刑警寧澤香府,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站码倦,受9級特大地震影響企孩,放射性物質發(fā)生泄漏。R本人自食惡果不足惜袁稽,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一勿璃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧推汽,春花似錦补疑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至暖夭,卻和暖如春锹杈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背迈着。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工嬉橙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寥假。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓市框,卻偏偏與公主長得像,于是被迫代替她去往敵國和親糕韧。 傳聞我的和親對象是個殘疾皇子枫振,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內容