AFNetworking源碼分析

說到AFNetwokring這個(gè)強(qiáng)大第三方網(wǎng)絡(luò)請(qǐng)求庫疫稿,大家應(yīng)該都不陌生吧,ios開發(fā)片挂、mac開發(fā)都經(jīng)常用,主要是他使用起來簡單闪湾、方便昵时。下面我們看看他的源碼,來探討一下吧湾蔓。

首先瘫析,我們一起來看一下它的框架的組成部分吧。


上面的圖片我從AFNetworking的文件中接的圖默责,我們可以看出贬循,包含5個(gè)部分,其實(shí)AFHTTPSessionManager是AFURLSessionManager的子類桃序,所以說它的組成是四個(gè)部分:

網(wǎng)絡(luò)通信模塊(AFHTTPSessionmanager杖虾、AFURLSessionManager)

網(wǎng)絡(luò)狀態(tài)監(jiān)聽模塊(AFNetworkReachabilityManager)

網(wǎng)絡(luò)通信信息序列化反序列化策略模塊(AFURLRequestSErialization、AFURLResponseSerialization)

網(wǎng)絡(luò)通信安全策略模塊(AFSecurityPolicy)

對(duì)ios UIKit庫的拓展

其核心當(dāng)然就是網(wǎng)絡(luò)通信模塊AFURLSessionManager媒熊,這個(gè)類是對(duì)NSURLSession的進(jìn)一步的封裝

其他模塊均是配合網(wǎng)絡(luò)通信或?qū)σ延蠻IKIt的擴(kuò)展

一奇适、初始化方法

最終都會(huì)到- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration方法中來了。

這個(gè)方法主要做了什么事情呢芦鳍?

1嚷往、調(diào)父類的- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration 這個(gè)方法里面設(shè)置了

隊(duì)列為非主線程隊(duì)列,隊(duì)列的并發(fā)數(shù)為1柠衅,

初始化了session皮仁,設(shè)置了代理,

初始化了網(wǎng)絡(luò)通信監(jiān)聽菲宴、網(wǎng)絡(luò)安全策略

贷祈,初始化了保存網(wǎng)絡(luò)請(qǐng)求任務(wù)、對(duì)應(yīng)的任務(wù)代理到字典中?

遍歷session中的任務(wù)喝峦,將任務(wù)對(duì)應(yīng)的任務(wù)代理設(shè)為nil势誊,請(qǐng)求代理

2、設(shè)置默認(rèn)AFURLRequestSErialization愈犹、AFURLResponseSerialization键科,分別是請(qǐng)求序列對(duì)象和響應(yīng)序列對(duì)象,這個(gè)兩個(gè)東西會(huì)在NSURLRequest中設(shè)置請(qǐng)求頭漩怎、請(qǐng)求體等一些信息中用到

上面的分析勋颖,可以看到,主要的東西還在父類中做的勋锤,里面初始化方法里便利session饭玲,將session中任務(wù)的代理清空是一種防御性編程。

然后來看GET請(qǐng)求

- (NSURLSessionDataTask *)GET:(NSString *)URLString參數(shù):(ID)參數(shù)進(jìn)度:(void(^)(NSProgress * _Nonnull))downloadProgress成功:(無效(^)(NSURLSessionDataTask * _Nonnull叁执,id _Nullable))成功失斍牙濉:(void(^)(NSURLSessionDataTask * _Nullable矮冬,NSError * _Nonnull))失敗

{//生成一個(gè)任務(wù)

NSURLSessionDataTask * dataTask = [self dataTaskWithHTTPMethod:@“GET”URLString:URLString參數(shù):參數(shù)上傳進(jìn)度:無downloadProgress:downloadProgress成功:成功失敗:失敗];

//開始網(wǎng)絡(luò)請(qǐng)求[dataTask resume];返回dataTask;}

這里主要生成一個(gè)NSURLSessionDataTask來進(jìn)行網(wǎng)絡(luò)請(qǐng)求

我們繼續(xù)往父類里看次哈,看看這個(gè)方法到底做了什么:

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)方法URLString:(NSString *)URLString參數(shù):(ID)參數(shù)uploadProgress:(可為空(void)(^)(NSProgress * uploadProgress))uploadProgressdownloadProgress :(可空(void)(^)(NSProgress * downloadProgress))downloadProgress成功:(void(^)(NSURLSessionDataTask *胎署,id))成功失敗:(void(^)(NSURLSessionDataTask *窑滞,NSError *))失敗{NSError * serializationError = nil;

//把參數(shù)琼牧,還有各種東西轉(zhuǎn)化為一個(gè)請(qǐng)求

NSMutableURLRequest * request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];

If(serializationError){如果(失敗)

{#pragma clang診斷推送#pragma clang診斷忽略“-WgnU”//如果解析錯(cuò)誤哀卫,直接返回dispatch_async(self.completionQueue巨坊?:dispatch_get_main_queue(),^ {失敶烁摹(nil趾撵,serializationError);});

#pragma clang診斷流行}返回零;}__block NSURLSessionDataTask * dataTask = nil;dataTask = [self dataTaskWithRequest:request上傳進(jìn)度:上傳進(jìn)度downloadProgress:downloadProgresscompletionHandler:^(NSURLResponse * __unused response,id responseObject共啃,NSError * error){如果(錯(cuò)誤){如果(失斦嫉鳌){失敗(dataTask勋磕,錯(cuò)誤);}} else {如果(成功){成功(dataTask妈候,responseObject);}}}];返回dataTask;}

這個(gè)

這個(gè)方法做了兩件事:

1、用self.requestSerializer和各種參數(shù)去獲取一個(gè)最終網(wǎng)絡(luò)請(qǐng)求需要的NSMutableURLRequest

2挂滓、調(diào)用另外一個(gè)方法dataTaskWithRequest去拿到我們需要NSURLSessionDataTask,并且在回掉中調(diào)用成功或失敗的回掉

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)方法URLString:(NSString *)URLString參數(shù):(ID)參數(shù)uploadProgress:(可為空(void)(^)(NSProgress * uploadProgress))uploadProgressdownloadProgress :(可空(void)(^)(NSProgress * downloadProgress))downloadProgress成功:(void(^)(NSURLSessionDataTask *苦银,id))成功失敗:(void(^)(NSURLSessionDataTask *赶站,NSError *))失敗{NSError * serializationError = nil;//把參數(shù)幔虏,還有各種東西轉(zhuǎn)化為一個(gè)請(qǐng)求NSMutableURLRequest * request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];if(serializationError){如果(失敗){#pragma clang診斷推送#pragma clang診斷忽略“-WgnU”//如果解析錯(cuò)誤贝椿,直接返回dispatch_async(self.completionQueue想括?:dispatch_get_main_queue(),^ {失斃硬(nil瑟蜈,serializationError);});#pragma clang診斷流行}返回零;}__block NSURLSessionDataTask * dataTask = nil;dataTask = [self dataTaskWithRequest:request上傳進(jìn)度:上傳進(jìn)度downloadProgress:downloadProgresscompletionHandler:^(NSURLResponse * __unused response,id responseObject渣窜,NSError * error){如果(錯(cuò)誤){如果(失斊谈){失敗(dataTask乔宿,錯(cuò)誤);}} else {如果(成功){成功(dataTask位迂,responseObject);}}}];返回dataTask;}

我們繼續(xù)往下看:當(dāng)解析錯(cuò)誤,我們直接調(diào)用failuer失敗模塊回去,里面有個(gè)self.completionQueue,這是我們自定義的掂林,這個(gè)是gcd隊(duì)列臣缀,如果設(shè)置就從這個(gè)隊(duì)列中回掉了,不從主隊(duì)列中回掉了

實(shí)際上這個(gè)隊(duì)列還是很有用泻帮,有些公司有自己的一套數(shù)據(jù)加密解密解析模式精置,所以我們回掉過來的數(shù)據(jù)并不想在主隊(duì)列,我們可以在這個(gè)隊(duì)列對(duì)數(shù)據(jù)進(jìn)行解析锣杂,然后在回到主線程中氯窍。

(NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)請(qǐng)求uploadProgress:(可為空(void)(^)(NSProgress * uploadProgress))uploadProgressBlockdownloadProgress:(可空(void)(^)(NSProgress * downloadProgress))downloadProgressBlockcompletionHandler:(可空(void)(^)(NSURLResponse *響應(yīng),id _Nullable響應(yīng)對(duì)象蹲堂,NSError * _Nullable錯(cuò)誤))completionHandler {__block NSURLSessionDataTask * dataTask = nil;//第一件事,創(chuàng)建NSURLSessionDataTask贝淤,里面適配了iOS8上以下taskIdentifiers柒竞,函數(shù)創(chuàng)建任務(wù)對(duì)象。//其實(shí)現(xiàn)應(yīng)該是因?yàn)閕OS 8.0以下版本中會(huì)并發(fā)地創(chuàng)建多個(gè)任務(wù)對(duì)象播聪,而同步有沒有好朽基,導(dǎo)致taskIdentifiers不唯一...這邊做了一個(gè)串行處理url_session_manager_create_task_safely(^ {dataTask = [self.session dataTaskWithRequest:request];});[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];返回dataTask;}

我們注意到這個(gè)方法非常簡單,就調(diào)用了一個(gè)url_session_manager_create_task_safely()函數(shù)离陶,傳了一個(gè)塊進(jìn)去座里就是iOS的原生生成dataTask的方法稼虎。此外,還調(diào)用了一個(gè)addDelegateForDataTask的方法招刨。

我們到這先到這個(gè)函數(shù)里去看看:

static void url_session_manager_create_task_safely(dispatch_block_t block){if(NSFoundationVersionNumber

方法非常簡單霎俩,關(guān)鍵是理解這么做的目的:為什么我們不直接去調(diào)用

dataTask = [self.session dataTaskWithRequest:request];

非要繞這么一圈,我們點(diǎn)進(jìn)去錯(cuò)誤日志里看看沉眶,原來這是為了適配iOS8上的以下打却,創(chuàng)建會(huì)話的時(shí)候,偶發(fā)的情況會(huì)出現(xiàn)會(huì)話的屬性taskIdentifier這個(gè)值不唯一谎倔,而這個(gè)taskIdentifier是我們后面來映射代表的關(guān)鍵柳击,所以它必須是唯一的。

具體原因應(yīng)該是NSURLSession內(nèi)部去生成任務(wù)的時(shí)候是用多線程并發(fā)去執(zhí)行的片习。想通了這一點(diǎn)捌肴,我們就很好解決了,我們只需要在iOS8上以下同步串行的去生成任務(wù)就可以防止這一問題發(fā)生(如果還是不理解同步串行的原因藕咏,可以看看注釋)状知。

題外話:很多同學(xué)都會(huì)抱怨為什么同步我從來用不到,看侈离,有用到的地方了吧试幽,很多東西不是沒用,而只是你想不到怎么用。

我們接著看到:

[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

調(diào)用到:

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTaskuploadProgress:(可為空(void)(^)(NSProgress * uploadProgress))uploadProgressBlockdownloadProgress:(可空(void)(^)(NSProgress * downloadProgress))downloadProgressBlockcompletionHandler:(void(^)(NSURLResponse * response铺坞,id responseObject起宽,NSError * error))completionHandler{AFURLSessionManagerTaskDelegate * delegate = [[AFURLSessionManagerTaskDelegate alloc] init];// AFURLSessionManagerTaskDelegate與AFURLSessionManager建立相互的關(guān)系delegate.manager = self;delegate.completionHandler = completionHandler;//這個(gè)taskDescriptionForSessionTasks用來發(fā)送開始和掛起通知的時(shí)候會(huì)用到,就是用這個(gè)值來張貼通知济榨,來兩者對(duì)應(yīng)dataTask.taskDescription = self.taskDescriptionForSessionTasks;// *****將AF委托對(duì)象與dataTask建立關(guān)系[self setDelegate:delegate forTask:dataTask];//設(shè)置AF委托的上傳進(jìn)度坯沪,下載進(jìn)度塊。delegate.uploadProgressBlock = uploadProgressBlock;delegate.downloadProgressBlock = downloadProgressBlock;}

總結(jié)一下:

1)這個(gè)方法擒滑,生成了一個(gè)AFURLSessionManagerTaskDelegate腐晾,這個(gè)其實(shí)就是AF的自定義代理。我們請(qǐng)求傳來的參數(shù)丐一,都賦值給這個(gè)AF的代理了藻糖。

2)delegate.manager = self;代理把AFURLSessionManager這個(gè)類作為屬性了,我們可以看到:

@屬性(非原子库车,弱)AFURLSessionManager *管理器;

這個(gè)屬性是弱引用的巨柒,所以不會(huì)存在循環(huán)引用的問題。

3)我們調(diào)用了[self setDelegate:delegate forTask:dataTask];

我們進(jìn)去看看這個(gè)方法做了什么:

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)委托forTask:(NSURLSessionTask *)任務(wù){(diào)//斷言柠衍,如果沒有這個(gè)參數(shù)洋满,調(diào)試下墜毀在這NSParameterAssert(任務(wù));NSParameterAssert(代表);//加鎖保證字典線程安全[self.lock lock];//將AF委托放入以taskIdentifier標(biāo)記的詞典中(同一個(gè)NSURLSession中的taskIdentifier是唯一的)self.mutableTaskDelegatesKeyedByTaskIdentifier [@(task.taskIdentifier)] = delegate;//為AF代表設(shè)置任務(wù)的進(jìn)度監(jiān)聽[委托setupProgressForTask:任務(wù)];//添加任務(wù)開始和暫停的通知[self addNotificationObserverForTask:task];[self.lock解鎖];}

這個(gè)方法主要就是把AF代理和任務(wù)建立映射,存在了一個(gè)我們事先聲明好的字典里珍坊。

而要加鎖的原因是因?yàn)楸旧砦覀冞@個(gè)字典屬性是可變的牺勾,是線程不安全的。而我們對(duì)這些方法的調(diào)用阵漏,確實(shí)是會(huì)在復(fù)雜的多線程環(huán)境中驻民,后面會(huì)仔細(xì)提到線程問題。

還有個(gè)[delegate setupProgressForTask:task];我們到方法里去看看:

- (void)setupProgressForTask:(NSURLSessionTask *)task {__weak __typeof __(task)weakTask = task;//拿到上傳下載期望的數(shù)據(jù)大小self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;//將上傳與下載進(jìn)度和任務(wù)綁定在一起袱饭,直接取消掛起恢復(fù)進(jìn)度條川无,可以取消...任務(wù)[self.uploadProgress setCaslable:YES];[self.uploadProgress setCancellationHandler:^ {__typeof __(weakTask)strongTask = weakTask;[strong任務(wù)取消];}];[self.uploadProgress setPausable:YES];[self.uploadProgress setPausingHandler:^ {__typeof __(weakTask)strongTask = weakTask;[strongTask暫停];}];如果([self.uploadProgress respondsToSelector:@selector(setResumingHandler :)]){[self.uploadProgress setResumingHandler:^ {__typeof __(weakTask)strongTask = weakTask;[strongTask簡歷];}];}[self.downloadProgress setCancellable:YES];[self.downloadProgress setCancellationHandler:^ {__typeof __(weakTask)strongTask = weakTask;[strong任務(wù)取消];}];[self.downloadProgress setPausable:YES];[self.downloadProgress setPausingHandler:^ {__typeof __(weakTask)strongTask = weakTask;[strongTask暫停];}];如果([self.downloadProgress respondsToSelector:@selector(setResumingHandler :)]){[self.downloadProgress setResumingHandler:^ {__typeof __(weakTask)strongTask = weakTask;[strongTask簡歷];}];}//觀察任務(wù)的這些屬性[任務(wù)addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))選項(xiàng):NSKeyValueObservingOptionNew上下文:NULL];[任務(wù)addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))選項(xiàng):NSKeyValueObservingOptionNew上下文:NULL];[任務(wù)addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesSent))選項(xiàng):NSKeyValueObservingOptionNew上下文:NULL];[任務(wù)addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))選項(xiàng):NSKeyValueObservingOptionNew上下文:NULL];//觀察進(jìn)度這兩個(gè)屬性[self.downloadProgress addObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))選項(xiàng):NSKeyValueObservingOptionNew上下文:NULL];[self.uploadProgress addObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))選項(xiàng):NSKeyValueObservingOptionNew上下文:NULL];}

這個(gè)方法也非常簡單,主要做了以下幾件事:

1)設(shè)置downloadProgress與uploadProgress的一些屬性虑乖,并且把兩個(gè)和任務(wù)的任務(wù)狀態(tài)綁定在了一起懦趋。注意這兩個(gè)都是NSProgress的實(shí)例對(duì)象,(這里可能又一群小伙楞在這了疹味,這是個(gè)什么...)簡單來說仅叫,這就是iOS7引進(jìn)的一個(gè)用來管理進(jìn)度的類,可以開始糙捺,暫停诫咱,取消,完整的對(duì)應(yīng)了任務(wù)的各種狀態(tài)洪灯,當(dāng)進(jìn)度進(jìn)行各種操作的時(shí)候坎缭,任務(wù)也會(huì)引發(fā)對(duì)應(yīng)操作。

2)給的任務(wù)和進(jìn)度的各個(gè)屬及添加志愿監(jiān)聽,至于監(jiān)聽了干什么用掏呼,我們接著往下看:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {//是任務(wù)如果([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]){//給進(jìn)度條賦新值如果([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]){self.downloadProgress.completedUnitCount = [更改[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 = [更改[NSKeyValueChangeNewKey] longLongValue];} else if([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]){self.uploadProgress.totalUnitCount = [change [NSKeyValueChangeNewKey] longLongValue];}}//上面的賦新值會(huì)觸發(fā)這兩個(gè)坏快,調(diào)用塊回調(diào),用戶拿到進(jìn)度else if([object isEqual:self.downloadProgress]){if(self.downloadProgressBlock){self.downloadProgressBlock(對(duì)象);}}else if([object isEqual:self.uploadProgress]){if(self.uploadProgressBlock){self.uploadProgressBlock(對(duì)象);}}}

方法非常簡單直觀憎夷,主要就是如果任務(wù)觸發(fā)志愿莽鸿,則給進(jìn)度進(jìn)度賦值,應(yīng)為賦值了拾给,所以會(huì)觸發(fā)進(jìn)步的志愿祥得,也會(huì)調(diào)用到這里,然后去執(zhí)行我們傳進(jìn)來的downloadProgressBlock和uploadProgressBlock蒋得。主要的作用就是為了讓進(jìn)度實(shí)時(shí)的傳遞级及。

主要是觀摩一下大神的寫代碼的結(jié)構(gòu),這個(gè)解耦的編程思想额衙,不愧是大神...

還有一點(diǎn)需要注意:我們之前的setProgress和這個(gè)志愿監(jiān)聽创千,都是在我們AF自定義的委托內(nèi)的,是有一個(gè)任務(wù)就會(huì)有一個(gè)代表的所以說我們是每個(gè)任務(wù)都會(huì)去監(jiān)聽這些屬性入偷,分別在各自的AF代理內(nèi)⌒涤矗看到這疏之,可能有些小伙伴會(huì)有點(diǎn)亂,沒關(guān)系暇咆。等整個(gè)講完之后我們還會(huì)詳細(xì)的去講捋一捋經(jīng)理锋爪,任務(wù),還有AF自定義代理三者之前的對(duì)應(yīng)關(guān)系爸业。

到這里我們整個(gè)對(duì)任務(wù)的處理就完成了其骄。

2,HTTPS認(rèn)證- (void)URLSession:(NSURLSession *)會(huì)話didReceiveChallenge:(NSURLAuthenticationChallenge *)挑戰(zhàn)completionHandler:(void(^)(NSURLSessionAuthChallengeDisposition disposition扯旷,NSURLCredential * credential))completionHandler{//挑戰(zhàn)處理類型為默認(rèn)/ *NSURLSessionAuthChallengePerformDefaultHandling:默認(rèn)方式處理NSURLSessionAuthChallengeUseCredential:使用指定的證書NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑戰(zhàn)* /NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;__block NSURLCredential * credential = nil;// sessionDidReceiveAuthenticationChallenge是自定義方法拯爽,用來如何應(yīng)對(duì)服務(wù)器端的認(rèn)證挑戰(zhàn)如果(self.sessionDidReceiveAuthenticationChallenge){disposition = self.sessionDidReceiveAuthenticationChallenge(session,challenge钧忽,&credential);} else {//此處服務(wù)器要求客戶端的接收認(rèn)證挑戰(zhàn)方法是NSURLAuthenticationMethodServerTrust//也就是說服務(wù)端需要客戶端返回一個(gè)根據(jù)認(rèn)證挑戰(zhàn)的保護(hù)空間提供的信任(即challenge.protectionSpace.serverTrust)產(chǎn)生的挑戰(zhàn)證書毯炮。//而這個(gè)證書就需要使用credentialForTrust:來創(chuàng)建一個(gè)NSURLCredential對(duì)象如果([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){//基于客戶端的安全策略來決定是否信任該服務(wù)器,不信任的話耸黑,也就沒必要響應(yīng)挑戰(zhàn)如果([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]){//創(chuàng)建挑戰(zhàn)證書(注:挑戰(zhàn)方式為UseCredential和PerformDefaultHandling都需要新建挑戰(zhàn)證書)credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];//確定挑戰(zhàn)的方式如果(憑證){//證書挑戰(zhàn)處置= NSURLSessionAuthChallengeUseCredential;} else {//默認(rèn)挑戰(zhàn)唯一區(qū)別桃煎,下面少了這一步!處置= NSURLSessionAuthChallengePerformDefaultHandling;}} else {//取消挑戰(zhàn)處置= NSURLSessionAuthChallengeCancelAuthenticationChallenge;}} else {//默認(rèn)挑戰(zhàn)方式處置= NSURLSessionAuthChallengePerformDefaultHandling;}}//完成挑戰(zhàn)if(completionHandler){completionHandler(處置大刊,憑證);}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末为迈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葫辐,老刑警劉巖搜锰,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異另患,居然都是意外死亡纽乱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門昆箕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸦列,“玉大人,你說我怎么就攤上這事鹏倘∈磬停” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵纤泵,是天一觀的道長骆姐。 經(jīng)常有香客問我,道長捏题,這世上最難降的妖魔是什么玻褪? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮公荧,結(jié)果婚禮上带射,老公的妹妹穿的比我還像新娘。我一直安慰自己循狰,他們只是感情好窟社,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绪钥,像睡著了一般灿里。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上程腹,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天匣吊,我揣著相機(jī)與錄音,去河邊找鬼寸潦。 笑死缀去,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的甸祭。 我是一名探鬼主播缕碎,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼池户!你這毒婦竟也來了咏雌?” 一聲冷哼從身側(cè)響起凡怎,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赊抖,沒想到半個(gè)月后统倒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氛雪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年房匆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片报亩。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浴鸿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出弦追,到底是詐尸還是另有隱情岳链,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布劲件,位于F島的核電站掸哑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏零远。R本人自食惡果不足惜苗分,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望牵辣。 院中可真熱鬧俭嘁,春花似錦、人聲如沸服猪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罢猪。三九已至,卻和暖如春叉瘩,著一層夾襖步出監(jiān)牢的瞬間膳帕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工薇缅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留危彩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓泳桦,卻偏偏與公主長得像汤徽,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子灸撰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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