AFNetWorking(3.0)中NSURLSession的使用

AFNetWoking(3.0)是一個著名的iOS第三方網(wǎng)絡(luò)通信框架厉亏,當(dāng)前最新的3.0版的實現(xiàn)完全基于NSURLSession進行了封裝,作為一個剛剛學(xué)習(xí)NSURLSession的小白,本文主要關(guān)注AFNetWorking(3.0)中使用NSURLSession進行網(wǎng)絡(luò)通信的邏輯。

NSURLSession是蘋果公司在iOS7.0后推出用來替代NSURLConnection進行網(wǎng)絡(luò)請求的API,有關(guān)NSURLSession的使用在我對官方文檔翻譯后總結(jié)的NSURLSession的使用中有介紹和說明楣嘁。

AFNetWorking(3.0)中進行網(wǎng)絡(luò)通信的類

在AFNetWorking(3.0)中,實現(xiàn)網(wǎng)絡(luò)通信功能的類主要有三個

  1. AFHTTPSessionManager
  2. AFURLSessionManager
  3. AFURLSessionManagerTaskDelegate

其中AFHTTPSessionManager是AFURLSessionManager的子類珍逸,它封裝了用戶對于HTTP請求參數(shù)的操作逐虚,用戶通過調(diào)用相關(guān)方法就能進行GET、POST谆膳、PUT等方法叭爱;
AFURLSessionManager是AFNetWorking(3.0)的核心,它封裝了幾乎所有與NSURLSession相關(guān)的方法漱病,并實現(xiàn)了NSURLSessionDelegate买雾、 NSURLSessionTaskDelegate,、NSURLSessionDataDelegate杨帽、NSURLSessionDownloadDelegate等委托的方法漓穿。
AFURLSessionManagerTaskDelegate也實現(xiàn)委托的方法,不過僅僅實現(xiàn)了部分睦尽,它的作用主要是管理上傳與下載任務(wù)的進度。

AFNetWorking(3.0)的網(wǎng)絡(luò)通信流程

1. 創(chuàng)建一個AFURLSessionManager對象

AFURLSessionManager封裝了關(guān)于NSURLSession對象的操作型雳,因此通過創(chuàng)建一個AFURLSessionManager對象就簡化了創(chuàng)建NSURLSession的流程当凡。

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
    //1山害、config為空設(shè)為默認config
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
    self.sessionConfiguration = configuration;

    //2、創(chuàng)建操作隊列沿量,設(shè)置最大并發(fā)數(shù)
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;

    //3浪慌、創(chuàng)建session
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    //4、創(chuàng)建響應(yīng)序列化朴则,安全策略权纤,任務(wù)識別關(guān)鍵字
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

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

    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    
    //5、創(chuàng)建鎖
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;

    //6乌妒、為session管理的所有任務(wù)設(shè)置關(guān)聯(lián)的AFURLSessionManagerTaskDelegate對象
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        //1汹想、數(shù)據(jù)任務(wù)
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }
        //2、上傳任務(wù)
        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }
        //3撤蚊、下載任務(wù)
        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];

    return self;
}

通過上述代碼可以看到AFURLSessionManager的初始化做了以下工作:

  1. 初始化會話配置古掏,默認的會話配置為defaultSessionConfiguration類型
  2. 初始化操作隊列,隊列并發(fā)數(shù)為1侦啸,即為串行隊列
  3. 通過會話配置和操作隊列創(chuàng)建NSURLSession對象槽唾,并將自身設(shè)為委托
  4. 創(chuàng)建網(wǎng)絡(luò)響應(yīng)序列化工具對象
  5. 定義了網(wǎng)絡(luò)通信的安全策略
  6. 初始化映射數(shù)組,在進行多個網(wǎng)絡(luò)請求任務(wù)時光涂,每個task都對應(yīng)著一個AFURLSessionManagerTaskDelegate對象
  7. 為session管理的所有任務(wù)設(shè)置關(guān)聯(lián)的AFURLSessionManagerTaskDelegate對象

2. 封裝網(wǎng)絡(luò)通信請求參數(shù)

有了NSURLSession對象之后庞萍,便可以進行網(wǎng)絡(luò)通信了,iOS提供了兩種方式忘闻,一種是通過URL直接進行網(wǎng)絡(luò)請求钝计,一種是采用NSURLRequest對象進行請求;AFNetWorking將請求統(tǒng)一封裝成NSURLRequest對象服赎。
使用NSURLSession進行網(wǎng)絡(luò)請求葵蒂,需要選擇相應(yīng)的任務(wù)類型:數(shù)據(jù)任務(wù)類型(NSURLSessionDataTask)、上傳任務(wù)類型(NSURLSessionUploadTask)和下載任務(wù)類型(NSURLSessionDownloadTask)重虑。
對于數(shù)據(jù)任務(wù)類型践付,AFNetWorking(3.0)通過建立子類AFHTTPSessionManager對AFURLSessionManager類進行了進一步的抽象和封裝。這樣用戶在使用時缺厉,針對請求方法的不同選擇不同的網(wǎng)絡(luò)請求方法永高,只需傳入以下參數(shù)便可進行網(wǎng)絡(luò)請求:

  • 請求地址
  • 請求參數(shù)
  • 處理請求成功的block
  • 處理請求失敗的block
  • 處理上傳進度block
  • 處理下載進度block

雖然AFHTTPSessionMananger繼承于AFURLSessionManager,但由于有關(guān)NSURLSession的配置與操作全部封裝在AFURLSessionManager中提针,所以AFHTTPSessionMananger所要做的工作其實并不多命爬,主要是

  1. 根據(jù)傳入的URL和請求參數(shù)創(chuàng)建和修改NSURLResquest對象
  2. 根據(jù)用戶選擇的網(wǎng)絡(luò)請求方法,調(diào)用NSURLSessionManager對應(yīng)的方法辐脖,并對返回的NSURLSessionDataTask進行操作饲宛,所以AFHTTPSessionManager主要是用來進行數(shù)據(jù)任務(wù)的,有關(guān)上傳和下載的任務(wù)并不AFHTTPSessionManager中進行嗜价。

在AFHTTPSessionManager中僅包含三個屬性:

  1. NSString類型的URL
  2. 對請求進行序列化操作的AFHTTPRequestSerializer對象艇抠,封裝了對NSURLResquest對象進行操作的方法
  3. 對響應(yīng)進行序列化操作的AFHTTPResponseSerializer對象幕庐,封裝了對NSURLResponse對象進行操作的方法
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;

AFHTTPSessionMananger提供了一些初始化的方法,這些方法都是基于如下方法:

- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration

該方法對AFHTTPSessionMananger進行初始化操作家淤,并傳入?yún)?shù)configuration給AFURLSessionManager的初始化方法

AFHTTPSessionMananger提供了常用的網(wǎng)絡(luò)請求方法异剥,如:GET、POST絮重、HEAD冤寿、PUT、PATCH青伤、DELETE方法督怜,用戶所能調(diào)用方法都基于AFHTTPSessionMananger的以下兩個方法:

//進行網(wǎng)絡(luò)請求操作
- (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;
                                         
//進行請求體包含block的POST請求
- (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;                                                                                                                           

對于上傳和下載任務(wù),開發(fā)者可以采用同樣的方式對請求參數(shù)進行封裝潮模,或者直接調(diào)用AFURLSessionManager中的請求方法亮蛔。

//上傳文件請求
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromFile:(NSURL *)fileURL
                                         progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
//上傳數(shù)據(jù)類型                                
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromData:(NSData *)bodyData
                                         progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
//上傳數(shù)據(jù)流
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                                 progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                        completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;

//下載請求
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                          destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler;
//恢復(fù)下載請求
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
                                                progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                             destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                       completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler;

3. NSURLSessionTask的創(chuàng)建

要使用NSURLSession對象進行網(wǎng)絡(luò)通信,需要用該對象調(diào)用請求方法返回一個NSURLSessionTask對象擎厢,通過task對象開啟網(wǎng)絡(luò)請求究流。AFURLSessionManager會在task開啟網(wǎng)絡(luò)請求之前,將task與AFURLSessionManagerTaskDelegate對象通過字典mutableTaskDelegatesKeyedByTaskIdentifier進行映射动遭,以確保每一個task對象都會有一個taskDelegate對象芬探。
AFURLSessionManagerTaskDelegate類的定義在AFURLSessionManager中,它的作用主要是管理網(wǎng)絡(luò)請求任務(wù)的進度厘惦,因此也實現(xiàn)了NSURLSessionTaskDelegate偷仿、 NSURLSessionDataDelegate、NSURLSessionDownloadDelegate的部分方法宵蕉。task對象與taskDelegate對象進行映射的過程如下:

  1. 調(diào)用NSURLSession對象的方法返回一個task對象
  2. 創(chuàng)建一個taskDelegate對象酝静,將傳入的有關(guān)進度和出路請求完成的block賦給taskDelegate對象
  3. 以task對象的taskIdentifier標(biāo)識符為key,將taskDelegate對象添加到字典中

這里有一個問題需要解決羡玛,就是如何保證task對象的標(biāo)識符是唯一的别智?
在第一節(jié)中初始化AFURLSessionManager對象時,創(chuàng)建session對象時的委托是self稼稿,也就是AFURLSessionManager對象本身薄榛,AFNetWorking(3.0)在進行網(wǎng)絡(luò)請求時,所有的task對象都由該session對象產(chǎn)生让歼,而由同一個session產(chǎn)生的多個task敞恋,它們的標(biāo)識符都是唯一的;不同的session產(chǎn)生的task谋右,task的標(biāo)識符則不一定唯一硬猫。這樣做就能夠保證在進行多個網(wǎng)絡(luò)請求時,對每個網(wǎng)絡(luò)請求的task進行單獨管理,例如多個下載任務(wù)在執(zhí)行啸蜜,用戶可以決定哪個任務(wù)暫停馏予,哪個任務(wù)繼續(xù)下載。字典添加映射代碼如下盔性,以下載為例:

- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
                          progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                       destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                 completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    //1、創(chuàng)建一個AFURLSessionManagerTaskDelegate對象呢岗,將block參數(shù)賦給delegate
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    //2冕香、返回task對象
    if (destination) {
        delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
            return destination(location, task.response);
        };
    }

    downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
    //3、將task對象與delegate進行關(guān)聯(lián)
    [self setDelegate:delegate forTask:downloadTask];

    delegate.downloadProgressBlock = downloadProgressBlock;
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    //以task的標(biāo)識符為key后豫,將delegate放入字典
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

這里又出現(xiàn)了另一個問題悉尾,既然所有的task都是由同一個session產(chǎn)生,而該session對象的委托確是AFURLSessionManager對象本身挫酿,網(wǎng)絡(luò)請求的執(zhí)行會調(diào)用AFURLSessionManager實現(xiàn)的方法构眯,如何通過AFURLSessionManagerTaskDelegate對象來控制網(wǎng)絡(luò)請求任務(wù)的進行?
前面說過早龟,AFURLSessionManagerTaskDelegate實現(xiàn)了委托的部分方法惫霸,這部分方法控制著網(wǎng)絡(luò)請求的任務(wù)進度,而在AFURLSessionManager中也實現(xiàn)了這些方法葱弟,當(dāng)系統(tǒng)調(diào)用AFURLSessionManager中的實現(xiàn)方法時壹店,AFURLSessionManager根據(jù)task的標(biāo)識符在字典中找到對應(yīng)的AFURLSessionManagerTaskDelegate對象,若AFURLSessionManagerTaskDelegate對象中實現(xiàn)了該方法芝加,則調(diào)用AFURLSessionManagerTaskDelegate中實現(xiàn)的方法硅卢。

4. 網(wǎng)絡(luò)請求的進度管理

網(wǎng)絡(luò)請求的進度管理是由AFURLSessionManagerTaskDelegate負責(zé)的,AFURLSessionManagerTaskDelegate具有以下屬性:

@property (nonatomic, weak) AFURLSessionManager *manager;  //指向AFURLSessionManager對象藏杖,能夠調(diào)用AFURLSessionManager中的block方法
@property (nonatomic, strong) NSMutableData *mutableData;  //存放網(wǎng)絡(luò)請求接收到的數(shù)據(jù)
@property (nonatomic, strong) NSProgress *uploadProgress;  //上傳任務(wù)進度
@property (nonatomic, strong) NSProgress *downloadProgress;  //下載任務(wù)進度
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; //下載完成回調(diào)
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; //上傳任務(wù)進度回調(diào)
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; //下載任務(wù)進度回調(diào)
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; //處理回調(diào) 

當(dāng)網(wǎng)絡(luò)進行網(wǎng)絡(luò)通信時将塑,AFURLSessionManagerTaskDelegate中實現(xiàn)的委托方法會返回進度信息并記錄到uploadProgress和downloadProgress中,同時接收到的數(shù)據(jù)也會存放到mutableData中蝌麸。為了能夠響應(yīng)進度信息的變化点寥,AFURLSessionManagerTaskDelegate則通過KVO的對進度信息監(jiān)聽,一旦進度信息發(fā)生改變祥楣,則可以調(diào)用回調(diào)進行處理开财,例如可以在回調(diào)中刷新主界面。這些回調(diào)函數(shù)都是在作為網(wǎng)絡(luò)請求的參數(shù)傳入到AFURLSessionManager中误褪,并由AFURLSessionManager創(chuàng)建任務(wù)時賦給AFURLSessionManagerTaskDelegate對象的责鳍。

//設(shè)置uploadProgress與downloadProgress,并添加觀察者
- (void)setupProgressForTask:(NSURLSessionTask *)task {
    //#1兽间、對downloadProgress和uploadProgress進行設(shè)置历葛,代碼略
    //#2、添加對downloadProgress和uploadProgress的KVO觀察者  
    [self.downloadProgress addObserver:self
                            forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                               options:NSKeyValueObservingOptionNew
                               context:NULL];
    [self.uploadProgress addObserver:self
                          forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                             options:NSKeyValueObservingOptionNew
                             context:NULL];
}

//移除觀察者
- (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
    [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}

//添加默認的監(jiān)聽方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
   //調(diào)用downloadProgress的回調(diào)
   if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
    //調(diào)用uploadProgress的回調(diào)
    else if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}

當(dāng)NSURLSessionTask結(jié)束時,總會調(diào)用委托方法URLSession:task:didCompleteWithError: ,該方法告訴委托當(dāng)前的網(wǎng)絡(luò)數(shù)據(jù)通信已完成恤溶。在AFNetWorking(3.0)中乓诽,該方法的主要工作如下:

  1. 從manager中獲取本次請求的response序列化信息,放入userInfo字典
  2. 將網(wǎng)絡(luò)通信接收的數(shù)據(jù)放入userInfo字典
  3. 若網(wǎng)絡(luò)通信錯誤咒程,則將error放入userInfo字典鸠天,同時使用處理回調(diào)進行后續(xù)處理,并發(fā)送通知
  4. 若網(wǎng)絡(luò)通信正常帐姻,則將response序列化信息進行解析稠集,放入userInfo字典中,同時調(diào)用處理回調(diào)進行后續(xù)處理饥瓷,并發(fā)送通知
- (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];
                });
            });
        });
    }
}

以上就是我閱讀AFNetWorking(3.0)的源碼后做的關(guān)于NSURLSession的使用的總結(jié)剥纷。AFNetWorking(3.0)作為一個成熟、穩(wěn)定的網(wǎng)絡(luò)請求框架呢铆,所涉及的知識也不局限于NSURLSession的使用晦鞋,還有許多內(nèi)容,如網(wǎng)絡(luò)狀態(tài)的監(jiān)測棺克、網(wǎng)絡(luò)請求的安全性處理悠垛,這些都非常值得我們花時間去學(xué)習(xí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娜谊,一起剝皮案震驚了整個濱河市鼎文,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌因俐,老刑警劉巖拇惋,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抹剩,居然都是意外死亡撑帖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門澳眷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胡嘿,“玉大人,你說我怎么就攤上這事钳踊≈缘校” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵拓瞪,是天一觀的道長缴罗。 經(jīng)常有香客問我,道長祭埂,這世上最難降的妖魔是什么面氓? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上舌界,老公的妹妹穿的比我還像新娘掘譬。我一直安慰自己,他們只是感情好呻拌,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布葱轩。 她就那樣靜靜地躺著,像睡著了一般藐握。 火紅的嫁衣襯著肌膚如雪酿箭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天趾娃,我揣著相機與錄音,去河邊找鬼缔御。 笑死抬闷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的耕突。 我是一名探鬼主播笤成,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼眷茁!你這毒婦竟也來了炕泳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤上祈,失蹤者是張志新(化名)和其女友劉穎培遵,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體登刺,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡籽腕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纸俭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皇耗。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖揍很,靈堂內(nèi)的尸體忽然破棺而出郎楼,到底是詐尸還是另有隱情,我是刑警寧澤窒悔,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布呜袁,位于F島的核電站,受9級特大地震影響简珠,放射性物質(zhì)發(fā)生泄漏傅寡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荐操。 院中可真熱鬧芜抒,春花似錦、人聲如沸托启。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屯耸。三九已至拐迁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疗绣,已是汗流浹背线召。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留多矮,地道東北人缓淹。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像塔逃,于是被迫代替她去往敵國和親讯壶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

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