【源碼解讀】AFNetWorking ─── 網(wǎng)絡(luò)請(qǐng)求

一. 前言

AFNetWorking的使用率非常高齐媒,這個(gè)不必多說蒲每。無論是AFNetWorking還是SDWebImage等涉及到網(wǎng)絡(luò)請(qǐng)求的,底層都是封裝了原生的網(wǎng)絡(luò)請(qǐng)求喻括。如果對(duì)原生的網(wǎng)絡(luò)請(qǐng)求還不熟悉的話邀杏,可以看看我之前寫的這篇文章【iOS小結(jié)】NSURLSession
AFNetWorking的功能很多唬血,我這篇主要解讀的是網(wǎng)絡(luò)請(qǐng)求(關(guān)于AFNetWorking怎么封裝網(wǎng)絡(luò)請(qǐng)求的知識(shí))望蜡。

二. AFNetWorking的網(wǎng)絡(luò)請(qǐng)求

網(wǎng)絡(luò)請(qǐng)求涉及的類主要是這兩個(gè):

  • AFHTTPSessionManager:擁有生成任務(wù),管理任務(wù)的功能拷恨,并提供HTTP方法的API接口
  • AFURLSessionManager:封裝了生成任務(wù)脖律,管理任務(wù)
網(wǎng)絡(luò)請(qǐng)求部分.png

我們先來看一個(gè)簡(jiǎn)單的POST請(qǐng)求,我們只需把urlStringparameters傳進(jìn)去腕侄,AFHTTPSessionManager就會(huì)創(chuàng)建一個(gè)POST的請(qǐng)求任務(wù)小泉,并把請(qǐng)求進(jìn)度芦疏,請(qǐng)求成功,請(qǐng)求失敗通過Block回調(diào)給你微姊。
簡(jiǎn)單來說酸茴,其內(nèi)部的實(shí)現(xiàn)其實(shí)是會(huì)通過這兩個(gè)參數(shù)生成對(duì)應(yīng)的NSURLRequest,并通過NSURLSession創(chuàng)建請(qǐng)求任務(wù)兢交,遵守相關(guān)的NSURLSession的協(xié)議薪捍,在對(duì)應(yīng)的協(xié)議方法里面進(jìn)行判斷處理,回調(diào)配喳。

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//設(shè)置URL
NSString *urlString = @"www.baidu.com/login/info";
//設(shè)置參數(shù)
NSDictionary *parameters = @{@"username":@"Tom",@"pwd":@"123"};
//發(fā)送post請(qǐng)求
[manager POST:urlString parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
    //上傳過程中的回調(diào)
} success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
    //成功的回調(diào)
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    //失敗的回調(diào)
}];

接下來我們來一步步分析:

AFHTTPSessionManager

當(dāng)使用AFNetWorking進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí)酪穿,我們會(huì)創(chuàng)建AFHTTPSessionManager對(duì)象,進(jìn)行對(duì)應(yīng)的HTTP請(qǐng)求界逛。AFHTTPSessionManager繼承于AFURLSessionManager昆稿,除了AFURLSessionManager的功能外,還提供HTTP方法的接口息拜。
以下紅框是AFHTTPSessionManager提供的方法溉潭,以及對(duì)應(yīng)的用途:


HTTP支持的方法.png

我們?cè)賮砜淳唧w提供的方法:

//GET方法
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(nullable id)parameters
                      success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
//GET方法(帶下載進(jìn)度)
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                            parameters:(nullable id)parameters
                              progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
                               success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                               failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

//HEAD方法
- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

//POST方法
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
//POST方法(帶上傳進(jìn)度)
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//POST方法(帶構(gòu)造體Block)→ 用于Multipart/form-data
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(nullable id)parameters
     constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
//POST方法(帶構(gòu)造體Block和上傳進(jìn)度)→ 用于Multipart/form-data
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
              constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

//PUT方法
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
                   parameters:(nullable id)parameters
                      success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

//PATCH方法
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
                     parameters:(nullable id)parameters
                        success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                        failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

//DELETE方法
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
                      parameters:(nullable id)parameters
                         success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                         failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

其中比較特殊的是POST方法,POST帶構(gòu)造體Block的方法是用于Multipart/form-data少欺,用于上傳文件喳瓣。具體原理我就不多解釋,可以看看這個(gè)例子AFNetworking文件上傳Multipart/form-data POST文件上傳詳解
赞别。
另外畏陕,這兩種方法創(chuàng)建的是uploadTask(通過文件流上傳)。在下面方法中通過請(qǐng)求序列化對(duì)象self.requestSerializer將url仿滔、參數(shù)和構(gòu)造體Block序列化成對(duì)應(yīng)的request惠毁,然后通過該request去創(chuàng)建對(duì)應(yīng)的uploadTask。

// POST請(qǐng)求(帶構(gòu)造體block崎页,帶進(jìn)度鞠绰,用于Multipart/form-data,上傳)
// 用于Multipart/form-data上傳的基礎(chǔ)方法
- (NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(id)parameters
     constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                      progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
                       success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                       failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
    NSError *serializationError = nil;
    //序列化request(構(gòu)建)
    NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
    //判斷序列化是否成功
    if (serializationError) {
        //失敗的話就通過failure回調(diào)飒焦,返回nil
        if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
#pragma clang diagnostic pop
        }

        return nil;
    }

    //創(chuàng)建uploadTask(通過文件流上傳文件)
    __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(task, error);
            }
        } else {
            if (success) {
                success(task, responseObject);
            }
        }
    }];
    [task resume];
    return task;
}

而其他方法創(chuàng)建的是dataTask蜈膨。也是通過請(qǐng)求序列化對(duì)象self.requestSerializer將url、參數(shù)序列化成對(duì)應(yīng)的request牺荠,然后通過該request去創(chuàng)建對(duì)應(yīng)的dataTask翁巍。

// 所有請(qǐng)求的基礎(chǔ)方法(通過改變method參數(shù))
- (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;
    //把參數(shù),還有各種東西轉(zhuǎn)換為一個(gè)request
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    
    //轉(zhuǎn)換錯(cuò)誤就在failure中回調(diào)
    if (serializationError) {
        if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            //如果解析錯(cuò)誤休雌,直接返回
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
#pragma clang diagnostic pop
        }

        return nil;
    }

    //生成一個(gè)dataTask
    __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;
}

總之灶壶,AFHTTPSessionManager封裝了幾種我們常用的網(wǎng)絡(luò)請(qǐng)求以及文件上傳的接口(當(dāng)然也是不全的,比如少了downloadTask杈曲,這邊就需要我們直接使用AFHTTPRequestSerializer去創(chuàng)建)例朱。
通過AFHTTPRequestSerializer對(duì)象通過我們傳入的參數(shù)轉(zhuǎn)化成對(duì)應(yīng)的NSMutableURLRequest孝情。然后通過request調(diào)用其父類AFURLSessionManager創(chuàng)建對(duì)應(yīng)的task。當(dāng)然也把對(duì)應(yīng)的success和failure等回調(diào)傳進(jìn)去洒嗤。

另外箫荡,AFHTTPSessionManager有三個(gè)屬性在初始化時(shí)會(huì)被創(chuàng)建,baseURL好理解渔隶,requestSerializer是用來序列化(構(gòu)建)request的羔挡,
responseSerializer是用來處理響應(yīng)數(shù)據(jù)的。requestSerializer是AFHTTPSessionManager特有的屬性间唉,因?yàn)锳FHTTPSessionManager賦值構(gòu)建request绞灼,然后生成對(duì)應(yīng)的task,而responseSerializer實(shí)際上是用在AFURLSessionManager呈野,因?yàn)樗沁呚?fù)責(zé)生成管理任務(wù)低矮,并且對(duì)結(jié)果進(jìn)行處理。

self.baseURL = url;
// 設(shè)置請(qǐng)求和響應(yīng)序列化對(duì)象
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
AFURLSessionManager

其實(shí)從AFHTTPSessionManager先分析被冒,再分析AFURLSessionManager是有弊端的军掂,因?yàn)锳FHTTPSessionManager繼承于AFURLSessionManager,所以AFHTTPSessionManager的很多功能只有留到講解AFURLSessionManager的時(shí)候才能說明昨悼。

之前AFHTTPSessionManager的分析說過蝗锥,AFURLSessionManager封裝了生成、管理任務(wù)率触。AFHTTPSessionManager只需要生成對(duì)應(yīng)request终议,通過request調(diào)用AFURLSessionManager生成task的方法。
AFURLSessionManager是一個(gè)怎么樣的類呢葱蝗?
這邊的探究分成兩部分:
① 提供什么功能(其實(shí)還是跟任務(wù)有關(guān))→看.h
② 怎么實(shí)現(xiàn)這些功能 →看.m

① AFURLSessionManager提供的功能

AFURLSessionManager提供的功能也是之前AFHTTPSessionManager未講到的功能穴张。AFURLSessionManager主要是封裝了生成任務(wù)和管理任務(wù),AFURLSessionManager實(shí)際上是NSURLSession對(duì)象的組合體两曼,提供的功能自然也是圍繞著任務(wù)皂甘。
AFURLSessionManager提供的功能主要有三種:
1)獲取正在執(zhí)行的所有任務(wù)、請(qǐng)求任務(wù)合愈、上傳任務(wù)叮贩、下載任務(wù)击狮。

@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;

2)提供多種創(chuàng)建task的方法(封裝了所有NSURLSession創(chuàng)建task的方法)
3)提供了對(duì)NSURLSession協(xié)議的自定義處理的Block佛析。(類似于代理)

② AFURLSessionManager怎么實(shí)現(xiàn)這些功能

AFURLSessionManager.m的內(nèi)容其實(shí)挺多的,分開來看其實(shí)主要有四部分內(nèi)容(最主要還是第四部分):
1)單例創(chuàng)建各種隊(duì)列的C函數(shù)彪蓬。定義各種key寸莫,Notification的值。定義各種Block档冬。(該部分也好理解膘茎,就是為了讓下面來使用)
2)_AFURLSessionTaskSwizzling(主要用來交換NSURLSessionTask的Resume和Suspend方法)→主要是為了添加通知的代碼桃纯?

+ (void)load {
    /**
     WARNING: 高能預(yù)警
     https://github.com/AFNetworking/AFNetworking/pull/2702
     */
    // 擔(dān)心以后iOS中不存在NSURLSessionTask
    if (NSClassFromString(@"NSURLSessionTask")) {
        /**
         iOS 7和iOS 8在NSURLSessionTask實(shí)現(xiàn)上有些許不同,這使得下面的代碼實(shí)現(xiàn)略顯trick
         關(guān)于這個(gè)問題披坏,大家做了很多Unit Test态坦,足以證明這個(gè)方法是可行的
         目前我們所知的:
            - NSURLSessionTasks是一組class的統(tǒng)稱,如果你僅僅使用提供的API來獲取NSURLSessionTask的class棒拂,并不一定返回的是你想要的那個(gè)(獲取NSURLSessionTask的class目的是為了獲取其resume方法)
            - 簡(jiǎn)單地使用[NSURLSessionTask class]并不起作用伞梯。你需要新建一個(gè)NSURLSession,并根據(jù)創(chuàng)建的session再構(gòu)建出一個(gè)NSURLSessionTask對(duì)象才行帚屉。
            - iOS 7上谜诫,localDataTask(下面代碼構(gòu)造出的NSURLSessionDataTask類型的變量,為了獲取對(duì)應(yīng)Class)的類型是 __NSCFLocalDataTask攻旦,__NSCFLocalDataTask繼承自__NSCFLocalSessionTask喻旷,__NSCFLocalSessionTask繼承自__NSCFURLSessionTask。
            - iOS 8上牢屋,localDataTask的類型為__NSCFLocalDataTask且预,__NSCFLocalDataTask繼承自__NSCFLocalSessionTask,__NSCFLocalSessionTask繼承自NSURLSessionTask
          - iOS 7上伟阔,__NSCFLocalSessionTask和__NSCFURLSessionTask是僅有的兩個(gè)實(shí)現(xiàn)了resume和suspend方法的類辣之,另外__NSCFLocalSessionTask中的resume和suspend并沒有調(diào)用其父類(即__NSCFURLSessionTask)方法,這也意味著兩個(gè)類的方法都需要進(jìn)行method swizzling皱炉。
            - iOS 8上怀估,NSURLSessionTask是唯一實(shí)現(xiàn)了resume和suspend方法的類。這也意味著其是唯一需要進(jìn)行method swizzling的類
            - 因?yàn)镹SURLSessionTask并不是在每個(gè)iOS版本中都存在合搅,所以把這些放在此處(即load函數(shù)中)多搀,比如給一個(gè)dummy class添加swizzled方法都會(huì)變得很方便,管理起來也方便灾部。
        
         一些假設(shè)前提:
            - 目前iOS中resume和suspend的方法實(shí)現(xiàn)中并沒有調(diào)用對(duì)應(yīng)的父類方法康铭。如果日后iOS改變了這種做法,我們還需要重新處理
            - 沒有哪個(gè)后臺(tái)task會(huì)重寫resume和suspend函數(shù)
         
         */
        // 1) 首先構(gòu)建一個(gè)NSURLSession對(duì)象session赌髓,再通過session構(gòu)建出一個(gè)_NSCFLocalDataTask變量
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
        NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
        // 2) 獲取到af_resume實(shí)現(xiàn)的指針
        IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
        Class currentClass = [localDataTask class];
        // 3) 檢查當(dāng)前class是否實(shí)現(xiàn)了resume从藤。如果實(shí)現(xiàn)了,繼續(xù)第4步锁蠕。
        while (class_getInstanceMethod(currentClass, @selector(resume))) {
            // 4) 獲取到當(dāng)前class的父類(superClass)
            Class superClass = [currentClass superclass];
            // 5) 獲取到當(dāng)前class對(duì)于resume實(shí)現(xiàn)的指針
            IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
            //  6) 獲取到父類對(duì)于resume實(shí)現(xiàn)的指針
            IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
            // 7) 如果當(dāng)前class對(duì)于resume的實(shí)現(xiàn)和父類不一樣(類似iOS7上的情況)夷野,并且當(dāng)前class的resume實(shí)現(xiàn)和af_resume不一樣,才進(jìn)行method swizzling荣倾。
            if (classResumeIMP != superclassResumeIMP &&
                originalAFResumeIMP != classResumeIMP) {
                [self swizzleResumeAndSuspendMethodForClass:currentClass];
            }
            // 8) 設(shè)置當(dāng)前操作的class為其父類class悯搔,重復(fù)步驟3~8
            currentClass = [currentClass superclass];
        }
        
        [localDataTask cancel];
        [session finishTasksAndInvalidate];
    }
}

+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
    Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
    Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));

    if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
        af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
    }

    if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
        af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
    }
}

- (NSURLSessionTaskState)state {
    //在實(shí)際的虛擬類中不應(yīng)調(diào)用狀態(tài)方法。
    NSAssert(NO, @"State method should never be called in the actual dummy class");
    // 初始狀態(tài)是NSURLSessionTaskStateCanceling;
    return NSURLSessionTaskStateCanceling;
}

- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    //執(zhí)行resume方法
    [self af_resume];
    
    //如果之前是其他狀態(tài)舌仍,就變回resume狀態(tài)妒貌,發(fā)出DidResume的通知
    if (state != NSURLSessionTaskStateRunning) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}

- (void)af_suspend {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    //執(zhí)行suspend方法
    [self af_suspend];
    
    //如果之前是其他狀態(tài)通危,就變回suspend狀態(tài),發(fā)出DidSuspend的通知
    if (state != NSURLSessionTaskStateSuspended) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
    }
}

3)AFURLSessionManagerTaskDelegate(task代理類)
為什么是task的代理類呢灌曙?主要是因?yàn)閠ask將它將處理獲取數(shù)據(jù)菊碟,監(jiān)聽進(jìn)度的職責(zé)放在該類去執(zhí)行,AFURLSessionManagerTaskDelegate是task的代理人在刺。我們先來看AFURLSessionManager里面的方法:(同理對(duì)應(yīng)的uploadTask和downloadTask也有設(shè)置的方法)

//設(shè)置普通請(qǐng)求task的代理人
- (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
{
    // 創(chuàng)建一個(gè)代理人
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    //為task設(shè)置代理(把dataTask傳給delegate)
    [self setDelegate:delegate forTask:dataTask];

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

可以說task該處理的回調(diào)都放在代理類這邊處理了框沟。我們?cè)倏纯催@個(gè)代理類怎么處理數(shù)據(jù)和監(jiān)聽進(jìn)度。

關(guān)于進(jìn)度的監(jiān)聽增炭,AFURLSessionManagerTaskDelegate設(shè)置了兩個(gè)NSProgress對(duì)象(uploadProgress和downloadProgress)來分別監(jiān)聽上傳和下載進(jìn)度忍燥。(關(guān)于NSProgress的作用可以看這篇文章進(jìn)度: NSProgress
簡(jiǎn)單說起來,NSProgress是一個(gè)記錄進(jìn)度的類隙姿,在需要記錄多個(gè)進(jìn)度有很大的優(yōu)勢(shì)梅垄。uploadProgress和downloadProgress除了記錄進(jìn)度外,通過設(shè)置也可以控制task的生命周期输玷。
另外队丝,通過對(duì)task接收到的字節(jié)數(shù)、期望接收到的字節(jié)數(shù)欲鹏、發(fā)送的字節(jié)數(shù)机久、期望發(fā)送的字節(jié)數(shù)進(jìn)行監(jiān)聽,值改變時(shí)去設(shè)置uploadProgress或downloadProgress對(duì)應(yīng)的總值或完成值赔嚎。通過對(duì)uploadProgress和downloadProgress完成度的監(jiān)聽膘盖,通過對(duì)應(yīng)的uploadProgressBlock和downloadProgressBlock將進(jìn)度信息回調(diào)出去。

//為task設(shè)置進(jìn)度(task的進(jìn)度會(huì)受到監(jiān)聽)
- (void)setupProgressForTask:(NSURLSessionTask *)task {
    __weak __typeof__(task) weakTask = task;

    //設(shè)置上傳和下載進(jìn)度條的總數(shù)目尤误,暫停侠畔,取消,回復(fù)回調(diào)
    self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
    self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
    [self.uploadProgress setCancellable:YES];
    [self.uploadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.uploadProgress setPausable:YES];
    [self.uploadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];
    if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.uploadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }

    [self.downloadProgress setCancellable:YES];
    [self.downloadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.downloadProgress setPausable:YES];
    [self.downloadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];

    if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.downloadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }

    //對(duì)task接收到的字節(jié)數(shù)损晤、期望接收到的字節(jié)數(shù)软棺、發(fā)送的字節(jié)數(shù)、期望發(fā)送的字節(jié)數(shù)進(jìn)行監(jiān)聽
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
              options:NSKeyValueObservingOptionNew
              context:NULL];

    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
              options:NSKeyValueObservingOptionNew
              context:NULL];

    //對(duì)上傳和下載完成的分?jǐn)?shù)(NSProgress的fractionCompleted)進(jìn)行監(jiān)聽
    [self.downloadProgress addObserver:self
                            forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                               options:NSKeyValueObservingOptionNew
                               context:NULL];
    [self.uploadProgress addObserver:self
                          forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                             options:NSKeyValueObservingOptionNew
                             context:NULL];
}
//移除task的進(jìn)度
- (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
    //移除task接收到的字節(jié)數(shù)尤勋、期望接收到的字節(jié)數(shù)喘落、發(fā)送的字節(jié)數(shù)、期望發(fā)送的字節(jié)數(shù)的監(jiān)聽
    [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
    [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))];
    [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
    [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))];
    
    //移除上傳最冰,下載進(jìn)度條完成的分?jǐn)?shù)的監(jiān)聽
    [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    //如果是task的監(jiān)聽
    if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
            self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
            self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
            self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
            self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        }
    }
    //如果是progress的監(jiān)聽
    else if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
    else if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}

關(guān)于數(shù)據(jù)的處理(當(dāng)然也包括對(duì)應(yīng)成功失敗的回調(diào))瘦棋,主要是實(shí)現(xiàn)那幾個(gè)有獲取到數(shù)據(jù)的NSURLSession的代理方法,這幾個(gè)代理方法是通過AFURLSessionManager來調(diào)用的锌奴,這個(gè)后續(xù)會(huì)提到兽狭。
關(guān)于數(shù)據(jù)最終的回調(diào)都是在- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error中處理憾股,另外比如dowmloadTask還需要處理下載中拼接數(shù)據(jù)和下載完成移動(dòng)文件夾的情況鹿蜀。
task完成時(shí)箕慧,除了將數(shù)據(jù)、響應(yīng)茴恰、錯(cuò)誤通過completionHandler回調(diào)出去颠焦,還發(fā)出DidComplete通知,傳出去userInfo(保存著請(qǐng)求和響應(yīng)序列化對(duì)象鈍往枣、數(shù)據(jù)伐庭、文件路徑、錯(cuò)誤信息等)分冈。有一點(diǎn)特別的是completionHandler會(huì)在線程組中回調(diào)圾另,我想這邊是為了讓開發(fā)者可以來設(shè)定監(jiān)聽。

#pragma mark - NSURLSessionTaskDelegate
//請(qǐng)求完成的回調(diào)
//處理完成后的數(shù)據(jù)
- (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];
    // 把響應(yīng)序列化對(duì)象保存在userInfo
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;

    //Performance Improvement from #2672
    NSData *data = nil;
    
    //把mutableData的值拿出來雕沉,并清空
    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;
    }

    // 把下載路徑和下載數(shù)據(jù)保存在userInfo
    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    // 如果完成的回調(diào)有錯(cuò)誤信息
    if (error) {
        // 把錯(cuò)誤信息也保存在userInfo
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;

        //把所有完成的回調(diào)放在completionQueue的線程組中
        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);
            }

            //在主線程發(fā)送任務(wù)完成的通知
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {//沒有錯(cuò)誤信息
        //在并行隊(duì)列異步執(zhí)行
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            //創(chuàng)建響應(yīng)對(duì)象(解析成我們需要的數(shù)據(jù))
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

            //如果有下載路徑就是下載集乔,只要返回下載路徑就可以
            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }

            // 把響應(yīng)對(duì)象保存在userInfo
            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }

            // 把序列化錯(cuò)誤信息保存在userInfo
            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }

            //把所有完成的回調(diào)放在completionQueue的線程組中
            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);
                }

                //發(fā)出任務(wù)完成的通知(userInfo附帶任務(wù)中的所有信息)
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
#pragma clang diagnostic pop
}

#pragma mark - NSURLSessionDataTaskDelegate
//開始受到數(shù)據(jù)的回調(diào)
- (void)URLSession:(__unused NSURLSession *)session
          dataTask:(__unused NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    //拼接數(shù)據(jù)
    [self.mutableData appendData:data];
}

#pragma mark - NSURLSessionDownloadTaskDelegate
//完成下載的回調(diào)
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    NSError *fileManagerError = nil;
    self.downloadFileURL = nil;

    if (self.downloadTaskDidFinishDownloading) {
        //獲取用戶指定的文件儲(chǔ)存的url地址
        self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        if (self.downloadFileURL) {
            //將儲(chǔ)存在臨時(shí)目錄下的文件移動(dòng)到用戶指定的url地址
            [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];

            //如果移動(dòng)失敗就發(fā)出通知(移動(dòng)失敗了。坡椒。扰路。)
            if (fileManagerError) {
                [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
            }
        }
    }
}

4)AFURLSessionManager(主要類,實(shí)現(xiàn)功能)
AFURLSessionManager包含一個(gè)NSURLSession屬性倔叼,生成任務(wù)本質(zhì)還是調(diào)用NSURLSession生成任務(wù)的方法,我們以創(chuàng)建dataTask為例汗唱,主要做了兩個(gè)步驟,加鎖通過session創(chuàng)建dataTask丈攒,為創(chuàng)建的dataTask設(shè)置代理人(AFURLSessionManagerTaskDelegate)哩罪。

#pragma mark - 普通請(qǐng)求任務(wù)
// 普通請(qǐng)求任務(wù)(帶完成的回調(diào))
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
}
// 普通請(qǐng)求任務(wù)(帶完成的回調(diào),以及上傳進(jìn)度和下載進(jìn)度的回調(diào))
- (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;
    
    //在串行隊(duì)列中同步生成task(類似加鎖)
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    //為生成的普通請(qǐng)求task添加代理
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

我們?cè)賮砜纯淳唧w怎么設(shè)置代理人巡验,這邊的代理跟我們之前的代理協(xié)議啥的不一樣识椰,這邊的代理人無非是AFURLSessionManager去調(diào)用代理類的方法(比如設(shè)置進(jìn)度,添加通知深碱,甚至后面的NSURLSession的代理方法)腹鹉。一個(gè)任務(wù)對(duì)應(yīng)一個(gè)代理類。另外敷硅,AFURLSessionManager還會(huì)將代理人保存在字典中功咒,key是對(duì)應(yīng)task的ID。
還有一點(diǎn)重要的是绞蹦,為task添加通知(TaskDidResume和DidSuspend)力奋,這樣就會(huì)剔除其他外來的任務(wù),讓該類產(chǎn)生的任務(wù)發(fā)出對(duì)應(yīng)的通知幽七。

//設(shè)置普通請(qǐng)求task的代理人
- (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
{
    // 創(chuàng)建一個(gè)代理人
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    //為task設(shè)置代理(把dataTask傳給delegate)
    [self setDelegate:delegate forTask:dataTask];

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

//為task設(shè)置代理
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    // 斷言判斷參數(shù)是否為空
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    // 以task的id為key景殷,AFURLSessionManagerTaskDelegate為value儲(chǔ)存起來
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    // 代理人為task設(shè)置進(jìn)度
    [delegate setupProgressForTask:task];
    //給task添加恢復(fù)和暫停的通知(AFNSURLSessionTaskDidResumeNotification和AFNSURLSessionTaskDidSuspendNotification)
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

那什么時(shí)候移除代理呢?那就是任務(wù)完成的時(shí)候。在這邊我們也可以看到task完成的代理方法執(zhí)行時(shí)猿挚,主動(dòng)去獲取對(duì)應(yīng)task的代理咐旧,讓他去執(zhí)行對(duì)應(yīng)操作。

//task完成時(shí)調(diào)用
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // 當(dāng)在后臺(tái)完成任務(wù)時(shí)绩蜻,委托可能是nil
    if (delegate) {
        // 調(diào)用代理對(duì)應(yīng)的方法(讓代理去處理)
        [delegate URLSession:session task:task didCompleteWithError:error];
        // task完成了铣墨,移除代理
        [self removeDelegateForTask:task];
    }

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

前面我們只是講了生成dataTask,對(duì)應(yīng)的還有downloadTask办绝,uploadTask步驟也差不多(NSURLSession生成task伊约,添加代理)。既然生成task孕蝉,也遵守了其協(xié)議屡律,肯定實(shí)現(xiàn)了NSURLSession的代理方法,就如上面提到的AFURLSessionManager提供了對(duì)NSURLSession協(xié)議的自定義處理的Block(類似于代理)降淮。
這個(gè)意思是在代理方法中你可以選擇默認(rèn)的處理或自定義處理(實(shí)現(xiàn)自定義處理的Block)疹尾。以收到dataTask收到數(shù)據(jù)的代理方法為例,默認(rèn)的處理是讓代理類去處理骤肛,進(jìn)行數(shù)據(jù)的拼接纳本,我們也可以實(shí)現(xiàn)dataTaskDidReceiveData,自己處理(這種如果用代理實(shí)現(xiàn)應(yīng)該好理解一點(diǎn))腋颠。

//dataTask收到數(shù)據(jù)時(shí)調(diào)用
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{

    //調(diào)用代理對(duì)應(yīng)的方法(讓代理去處理)
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
    [delegate URLSession:session dataTask:dataTask didReceiveData:data];

    //自己處理
    if (self.dataTaskDidReceiveData) {
        self.dataTaskDidReceiveData(session, dataTask, data);
    }
}

關(guān)于上面提到的(2)和(3)功能都說完了繁成,我們來說說第一個(gè)功能(1)獲取正在執(zhí)行的所有任務(wù)、請(qǐng)求任務(wù)淑玫、上傳任務(wù)巾腕、下載任務(wù)。其實(shí)就是使用session的getTasksWithCompletionHandler方法絮蒿。

#pragma mark -  獲取當(dāng)前運(yùn)行的任務(wù)
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
    __block NSArray *tasks = nil;
    // 創(chuàng)建一個(gè)信號(hào)量尊搬,值為0
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
            tasks = dataTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
            tasks = uploadTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
            tasks = downloadTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
            tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
        }

        //信號(hào)量加1
        dispatch_semaphore_signal(semaphore);
    }];

    //一直等待,知道信號(hào)量不為0才會(huì)執(zhí)行到這一步
    //將計(jì)數(shù)值減1,并且執(zhí)行返回
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return tasks;
}

// 獲取當(dāng)前正在運(yùn)行的tasks
- (NSArray *)tasks {
    return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
// 獲取當(dāng)前正在運(yùn)行的dataTasks
- (NSArray *)dataTasks {
    return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
// 獲取當(dāng)前正在運(yùn)行的uploadTasks
- (NSArray *)uploadTasks {
    return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
// 獲取當(dāng)前正在運(yùn)行的downloadTasks
- (NSArray *)downloadTasks {
    return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末土涝,一起剝皮案震驚了整個(gè)濱河市佛寿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌但壮,老刑警劉巖冀泻,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蜡饵,居然都是意外死亡弹渔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門溯祸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肢专,“玉大人舞肆,你說我怎么就攤上這事〔┱龋” “怎么了椿胯?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)欧募。 經(jīng)常有香客問我,道長(zhǎng)仆抵,這世上最難降的妖魔是什么跟继? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮镣丑,結(jié)果婚禮上舔糖,老公的妹妹穿的比我還像新娘。我一直安慰自己莺匠,他們只是感情好金吗,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著趣竣,像睡著了一般摇庙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遥缕,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天卫袒,我揣著相機(jī)與錄音,去河邊找鬼单匣。 笑死夕凝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的户秤。 我是一名探鬼主播码秉,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鸡号!你這毒婦竟也來了转砖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤鲸伴,失蹤者是張志新(化名)和其女友劉穎堪藐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挑围,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡礁竞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杉辙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片模捂。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出狂男,到底是詐尸還是另有隱情综看,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布岖食,位于F島的核電站红碑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏泡垃。R本人自食惡果不足惜析珊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蔑穴。 院中可真熱鬧忠寻,春花似錦、人聲如沸存和。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捐腿。三九已至纵朋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茄袖,已是汗流浹背倡蝙。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绞佩,地道東北人寺鸥。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像品山,于是被迫代替她去往敵國和親胆建。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容