YTKNetwork集成教程以及相關(guān)問(wèn)題思考

DEMO

https://github.com/albertjson/SZCEvolution

YTKNetwork介紹

YTKNetwork 是猿題庫(kù) iOS 研發(fā)團(tuán)隊(duì)基于?AFNetworking?封裝的 iOS 網(wǎng)絡(luò)庫(kù),其實(shí)現(xiàn)了一套 High Level 的 API,提供了更高層次的網(wǎng)絡(luò)訪問(wèn)抽象。目前在 GitHub 上已有 3600+ star ,是 Network 中的新星。

YTKNetwork提供的主要功能

支持按時(shí)間緩存和版本號(hào)緩存網(wǎng)絡(luò)請(qǐng)求內(nèi)容

支持統(tǒng)一設(shè)置服務(wù)器和 CDN 的地址

支持檢查返回 JSON 內(nèi)容的合法性

支持?block?和?delegate?兩種模式的回調(diào)方式

支持批量的網(wǎng)絡(luò)請(qǐng)求發(fā)送,并統(tǒng)一設(shè)置它們的回調(diào)(實(shí)現(xiàn)在?YTKBatchRequest?類中)

支持方便地設(shè)置有相互依賴的網(wǎng)絡(luò)請(qǐng)求的發(fā)送,例如:發(fā)送請(qǐng)求 A,根據(jù)請(qǐng)求 A 的結(jié)果,選擇性的發(fā)送請(qǐng)求 B 和 C,再根據(jù) B 和 C 的結(jié)果,選擇性的發(fā)送請(qǐng)求 D。(實(shí)現(xiàn)在?YTKChainRequest?類中)

支持網(wǎng)絡(luò)請(qǐng)求 URL 的 filter,可以統(tǒng)一為網(wǎng)絡(luò)請(qǐng)求加上一些參數(shù),或者修改一些路徑。

定義了一套插件機(jī)制,可以很方便地為 YTKNetwork 增加功能。猿題庫(kù)官方現(xiàn)在提供了一個(gè)插件,可以在某些網(wǎng)絡(luò)請(qǐng)求發(fā)起時(shí),在界面上顯示“正在加載”的 HUD。

YTKNetwork 的基本思想

YTKNetwork 的基本的思想是把每一個(gè)網(wǎng)絡(luò)請(qǐng)求封裝成對(duì)象。所以使用 YTKNetwork,你的每一個(gè)請(qǐng)求都需要繼承 YTKRequest 類,通過(guò)覆蓋父類的一些方法來(lái)構(gòu)造指定的網(wǎng)絡(luò)請(qǐng)求。

把每一個(gè)網(wǎng)絡(luò)請(qǐng)求封裝成對(duì)象其實(shí)是使用了設(shè)計(jì)模式中的 Command 模式,它有以下好處:

將網(wǎng)絡(luò)請(qǐng)求與具體的第三方庫(kù)依賴隔離,方便以后更換底層的網(wǎng)絡(luò)庫(kù)。

方便在基類中處理公共邏輯,例如猿題庫(kù)的數(shù)據(jù)版本號(hào)信息就統(tǒng)一在基類中處理。

方便在基類中處理緩存邏輯,以及其它一些公共邏輯。

方便做對(duì)象的持久化。

當(dāng)然,如果說(shuō)它有什么不好,那就是如果你的工程非常簡(jiǎn)單,這么寫(xiě)會(huì)顯得沒(méi)有直接用?AFNetworking?將請(qǐng)求邏輯寫(xiě)在 Controller 中方便,所以 YTKNetwork 并不合適特別簡(jiǎn)單的項(xiàng)目。

關(guān)于集約式和離散式

集約式

介紹:即項(xiàng)目中的每個(gè)請(qǐng)求都會(huì)走統(tǒng)一的入口,對(duì)外暴露了請(qǐng)求的 URL 和 Param 以及請(qǐng)求方式,入口一般都是通過(guò)單例?來(lái)實(shí)現(xiàn),AFNetworking 的官方 demo 就是采用的集約式的方式對(duì)網(wǎng)絡(luò)請(qǐng)求進(jìn)行的封裝,也是目前比較流行的網(wǎng)絡(luò)請(qǐng)求方式。

優(yōu)點(diǎn)

使用便捷,能實(shí)現(xiàn)快速開(kāi)發(fā)

缺點(diǎn)

對(duì)每個(gè)請(qǐng)求的定制型不夠強(qiáng)

不方便后期業(yè)務(wù)拓展

離散式

介紹:即每個(gè)網(wǎng)絡(luò)請(qǐng)求類都是一個(gè)對(duì)象,它的 URL 以及請(qǐng)求方式和響應(yīng)方式 均不暴露給外部調(diào)用。只能內(nèi)部通過(guò)?重載或?qū)崿F(xiàn)協(xié)議?的方式來(lái)指定,外部調(diào)用只需要傳 Param 即可,YTKNetwork就是采用的這種網(wǎng)絡(luò)請(qǐng)求方式。

優(yōu)點(diǎn)

URL 以及請(qǐng)求和響應(yīng)方式不暴露給外部,避免外部調(diào)用的時(shí)候?qū)戝e(cuò)

業(yè)務(wù)方使用起來(lái)較簡(jiǎn)單,業(yè)務(wù)使用者不需要去關(guān)心它的內(nèi)部實(shí)現(xiàn)

可定制性強(qiáng),可以為每個(gè)請(qǐng)求指定請(qǐng)求的超時(shí)時(shí)間以及緩存的周期

缺點(diǎn)

網(wǎng)絡(luò)層需要業(yè)務(wù)實(shí)現(xiàn)方去寫(xiě),變相的增加了部分工作量

文件增多,程序包會(huì)變大[倒也不是特別大]

在微脈的iOS客戶端,由于最初人員較少,且業(yè)務(wù)變更較頻繁。故使用的就是集約式請(qǐng)求。不過(guò)考慮到為實(shí)現(xiàn)業(yè)務(wù)便捷性以及可拓展性,故增加了?RequestHeader?請(qǐng)求頭,以及?WMHttpHelper?網(wǎng)絡(luò)操作工具類?;旧弦褲M足于目前的開(kāi)發(fā)模式

不過(guò)長(zhǎng)遠(yuǎn)來(lái)看,轉(zhuǎn)成離散式的網(wǎng)絡(luò)請(qǐng)求也是有必要的。

安裝

你可以在 Podfile 中加入下面一行代碼來(lái)使用 YTKNetwork

pod 'YTKNetwork'

集成至項(xiàng)目

項(xiàng)目文件介紹

YTKNetwork源碼

YTKBaseRequest:為請(qǐng)求的基類,內(nèi)部聲明了請(qǐng)求的常用 API :

比如請(qǐng)求方式,請(qǐng)求解析方式,響應(yīng)解析方式,請(qǐng)求參數(shù)等等。它的用意是讓子類去實(shí)現(xiàn)的,本身不做實(shí)現(xiàn)。

YTKRequest:是?YTKBaseRequest?的子類,在其基礎(chǔ)上支持了緩存,并且提供了豐富的緩存策略?;旧享?xiàng)目中使用都是繼承于?YTKRequest?去寫(xiě)業(yè)務(wù)的 Request。

YTKNetworkAgent:真正做網(wǎng)絡(luò)請(qǐng)求的類,在內(nèi)部跟?AFNetworking?直接交互,調(diào)用了?AFNetworking?提供的各種請(qǐng)求,當(dāng)然,如果底層想切換其他第三方,在這個(gè)類中替換掉就行了。

YTKNetworkConfig:該文件為網(wǎng)絡(luò)請(qǐng)求的統(tǒng)一配置類,提供了設(shè)置?baseUrl?cdnUrl?等基礎(chǔ)請(qǐng)求路徑,可以給所有的請(qǐng)求增加參數(shù)等等。

YTKBatchRequest:為批量進(jìn)行網(wǎng)絡(luò)請(qǐng)求而生,提供了代理和 block 兩種方式給外部使用

YTKChainRequest:當(dāng)多個(gè)請(qǐng)求之間有關(guān)聯(lián)的時(shí)候采用此類去實(shí)現(xiàn)非常方便,即下一個(gè)請(qǐng)求可能要根據(jù)上個(gè)請(qǐng)求返回的數(shù)據(jù)進(jìn)行請(qǐng)求。

YTKBatchRequestAgent,YTKChainRequestAgent:分別是?YTKBatchRequest,YTKChainRequest?的操作類,不需要也無(wú)妨主動(dòng)調(diào)用

集成文件介紹

My Project Table

這是 Demo 工程的我新增的文件,一般情況下,不建議直接繼承于?YTKRequest?類去寫(xiě)業(yè)務(wù),需要自己寫(xiě)請(qǐng)求的基類,具體業(yè)務(wù)請(qǐng)求再繼承于改項(xiàng)目基類,避免因新版本 YTKRequest 中修改了部分實(shí)現(xiàn)的默認(rèn)值導(dǎo)致的程序需要做大量的修改。其中:ZCBaseRequest,ZCBatchRequest,ZCChainRequest?就是 demo 項(xiàng)目的基類。ZCJSONModel?是 JSON 轉(zhuǎn) Model 的基類,而?ZCHTTPError?是用于自定義錯(cuò)誤信息的

single http example

這是 Demo 工程中具體某個(gè)請(qǐng)求的實(shí)例。這種展現(xiàn)方式很清晰,ZCGetInfoParam?是請(qǐng)求的入?yún)㈩?,ZCMeGetInfoManger是具體的請(qǐng)求操作類,?ZCGetInfoModel是出參類。不過(guò)如果入?yún)⒑统鰠⒑苌?,可以只有一個(gè)manger類

相關(guān)問(wèn)題思考

我這里不想介紹?YTKNetwork?的基礎(chǔ)和高級(jí)使用教程。如果想了解基礎(chǔ)以及高級(jí)使用教程可以看這里

YTKNetwork 使用基礎(chǔ)教程

YTKNetwork 使用高級(jí)教程

這篇文章重在介紹集成以及使用過(guò)程中遇到一些問(wèn)題以及解決方案

1>JSON轉(zhuǎn)Model的問(wèn)題

對(duì)于稍微復(fù)雜的項(xiàng)目,可能某些接口返回?cái)?shù)據(jù)有十多個(gè),使用的時(shí)候不可能從字典中一個(gè)一個(gè)讀取出來(lái),然后再做??空處理,一般都是采用轉(zhuǎn) Model 的方式 轉(zhuǎn)換成具體的業(yè)務(wù)模型,從業(yè)務(wù)模型中獲取具體數(shù)據(jù),常見(jiàn)的有?JSONModel,Mantle,MJExtension?等第三方庫(kù),本文以JSONModel為例,來(lái)實(shí)現(xiàn)框架內(nèi)部解析成 Model

在?YTKBaseRequest?新增 JSONModel 屬性

/// JsonModel類

@property (nonatomic, strong, readonly, nullable) id? responseJSONModel;

在?YTKBaseRequest?新增 modelClass 函數(shù),用于子類去實(shí)現(xiàn),表明要轉(zhuǎn)換的具體 model 類的類名

YTKBaseRequest.h

/// model對(duì)應(yīng)的類,子類實(shí)現(xiàn)的話會(huì)直接映射到該model類并進(jìn)行初始化操作

- (Class)modelClass;

YTKBaseRequest.m

- (Class)modelClass

{

? return nil;

}

查看源碼不難發(fā)現(xiàn),真正處理網(wǎng)絡(luò)請(qǐng)求成功和失敗的地方是?YTKNetworkAgent?類,在?- (void)handleRequestResult:(NSURLSessionTask *)task responseObject:(id)responseObject error:(NSError *)error?和?- (void)requestDidSucceedWithRequest:(YTKBaseRequest *)request,- (void)requestDidFailWithRequest:(YTKBaseRequest *)request error:(NSError *)error?這三個(gè)方法。

具體操作為

- (void)requestDidSucceedWithRequest:(YTKBaseRequest *)request {

@autoreleasepool {

? ? [request requestCompletePreprocessor];

? ? [self JSONConvertModel:request];

}

dispatch_async(dispatch_get_main_queue(), ^{

? ? [request toggleAccessoriesWillStopCallBack];

? ? [request requestCompleteFilter];

? ? if (request.delegate != nil) {

? ? ? ? [request.delegate requestFinished:request];

? ? }

? ? if (request.successCompletionBlock) {

? ? ? ? request.successCompletionBlock(request);

? ? }

? ? [request toggleAccessoriesDidStopCallBack];

});

}

///json轉(zhuǎn)model的具體方法

- (void)JSONConvertModel:(YTKBaseRequest*)request

{

Class modelClass = [request modelClass];

if (!modelClass) {

? ? return;

}

NSError * error = nil;

if ([request.responseJSONObject isKindOfClass:[NSDictionary class]]) {

? ? request.responseJSONModel = [[modelClass alloc] initWithDictionary:request.responseJSONObject error:&error];

}else if ([request.responseJSONObject isKindOfClass:[NSArray class]]){

? ? request.responseJSONModel = [modelClass arrayOfModelsFromDictionaries:request.responseJSONObject error:&error];

}else if {

? ? //這里不做處理,因?yàn)锳FNetworking如果返回的數(shù)據(jù)為null的時(shí)候會(huì)調(diào)用失敗的回調(diào)

}

if (error) {

? ? YTKLog(@"Request JSON---JSONModel Failed =%@",error);

}

}

在?YTKRequest?類中也需要新增緩存類的model,具體代碼為

YTKRequest.m

@property (nonatomic, strong) id cacheJSONModel;///TTT

- (id)responseJSONModel {

if (_cacheJSONModel) {

? ? return _cacheJSONModel;

}

return [super responseJSONModel];

}

- (BOOL)loadCacheData {

NSString *path = [self cacheFilePath];

NSFileManager *fileManager = [NSFileManager defaultManager];

NSError *error = nil;

if ([fileManager fileExistsAtPath:path isDirectory:nil]) {

? ? NSData *data = [NSData dataWithContentsOfFile:path];

? ? _cacheData = data;

? ? _cacheString = [[NSString alloc] initWithData:_cacheData encoding:self.cacheMetadata.stringEncoding];

? ? switch (self.responseSerializerType) {

? ? ? ? case YTKResponseSerializerTypeHTTP:

? ? ? ? ? ? // Do nothing.

? ? ? ? ? ? return YES;

? ? ? ? case YTKResponseSerializerTypeJSON:

? ? ? ? ? ? _cacheJSON = [NSJSONSerialization JSONObjectWithData:_cacheData options:(NSJSONReadingOptions)0 error:&error];

? ? ? ? ? ? if (!error) {

? ? ? ? ? ? ? ? [self JSONConvertModel:_cacheJSON];

? ? ? ? ? ? }

? ? ? ? ? ? return error == nil;

? ? ? ? case YTKResponseSerializerTypeXMLParser:

? ? ? ? ? ? _cacheXML = [[NSXMLParser alloc] initWithData:_cacheData];

? ? ? ? ? ? return YES;

? ? }

}

return NO;

}

- (void)JSONConvertModel:(YTKBaseRequest*)request

{

? ///跟第二步的實(shí)現(xiàn)方式一樣

}

到這里基本上已經(jīng)實(shí)現(xiàn)了 json-model,具體的業(yè)務(wù)代碼為:

- (void)loadCacheData {

NSString *userId = @"1";

GetUserInfoApi *api = [[GetUserInfoApi alloc] initWithUserId:userId];

if ([api loadCacheWithError:nil]) {

? ? NSDictionary *json = [api responseJSONObject];

? ? NSLog(@"json = %@", json);

? ? // show cached data

? ? YTKJSONModel * model = [api responseJSONModel];

? ? NSLog(@"jsonmodelllll=%@---%@",model.nick,model.level);

}

api.animatingText = @"正在加載";

api.animatingView = self.view;

[api startWithCompletionBlockWithSuccess:^(YTKBaseRequest *request) {

? ? NSLog(@"update ui=%@",[api responseJSONModel]);

} failure:^(YTKBaseRequest *request) {

? ? NSLog(@"failed");

}];

}

大致步驟如此,只是這樣實(shí)現(xiàn)的話需要修改源代碼,細(xì)節(jié)可以參考 demo,地址:https://github.com/albertjson/YTKNetwork

2>token引發(fā)的問(wèn)題

一般情況下,網(wǎng)絡(luò)請(qǐng)求客戶端都要帶 token,用于服務(wù)端驗(yàn)證用戶的登陸有效性。那么 token 失效可能需要做一些處理,在 demo 中這部分驗(yàn)證是寫(xiě)在?ZCBaseRequest?類中實(shí)現(xiàn)的。這樣避免業(yè)務(wù)代碼在各處進(jìn)行處理 token 失效的情況

- (void)requestFailedFilter

{

? ? [super requestFailedFilter];

? ? if (error.code==TokenTimeOut) {

? ? ? ? ......

? ? }

}

當(dāng)然,這樣處理之后,如果子類需要在錯(cuò)誤的時(shí)候做特殊處理,那么在重寫(xiě)?requestFailedFilter?方法的時(shí)候一定要調(diào)用?[super requestFailedFilter]

3>錯(cuò)誤解析

YTKNetwork 調(diào)用 HTTP 返回錯(cuò)誤的類為 NSError。而自己的項(xiàng)目一般都需要定制錯(cuò)誤信息,或者根據(jù)某一類型的錯(cuò)誤進(jìn)行特殊的操作。這一步可以在自己定義的請(qǐng)求基類的錯(cuò)誤回調(diào)中處理。我們先來(lái)看一段 YTKNetwork 的源碼:

- (void)handleRequestResult:(NSURLSessionTask *)task responseObject:(id)responseObject error:(NSError *)error {

? ? //這里只留下關(guān)鍵性代碼

? ? NSError * __autoreleasing serializationError = nil;

? ? NSError * __autoreleasing validationError = nil;

? ? NSError *requestError = nil;

? ? BOOL succeed = NO;

? ? request.responseObject = responseObject;

? ? if ([request.responseObject isKindOfClass:[NSData class]]) {

? ? ? ? request.responseData = responseObject;

? ? ? ? request.responseString = [[NSString alloc] initWithData:responseObject encoding:[YTKNetworkUtils stringEncodingWithRequest:request]];

? ? ? ? switch (request.responseSerializerType) {

? ? ? ? ? ? case YTKResponseSerializerTypeHTTP:

? ? ? ? ? ? ? ? // Default serializer. Do nothing.

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case YTKResponseSerializerTypeJSON:

? ? ? ? ? ? ? ? request.responseObject = [self.jsonResponseSerializer responseObjectForResponse:task.response data:request.responseData error:&serializationError];

? ? ? ? ? ? ? ? request.responseJSONObject = request.responseObject;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case YTKResponseSerializerTypeXMLParser:

? ? ? ? ? ? ? ? request.responseObject = [self.xmlParserResponseSerialzier responseObjectForResponse:task.response data:request.responseData error:&serializationError];

? ? ? ? ? ? ? ? break;

? ? ? ? }

? ? }

? ? if (error) {

? ? ? ? succeed = NO;

? ? ? ? requestError = error;

? ? } else if (serializationError) {

? ? ? ? succeed = NO;

? ? ? ? requestError = serializationError;

? ? } else {

? ? ? ? succeed = [self validateResult:request error:&validationError];

? ? ? ? requestError = validationError;

? ? }

? ? //只留關(guān)鍵性代碼

}

不難發(fā)現(xiàn),這里的錯(cuò)誤其實(shí)分三類

requestError:請(qǐng)求錯(cuò)誤,為 AFNetworking 進(jìn)行網(wǎng)絡(luò)請(qǐng)求的請(qǐng)求錯(cuò)誤,比如說(shuō)沒(méi)網(wǎng)絡(luò)。

serializationError:響應(yīng)錯(cuò)誤,為 AFNetworking 響應(yīng)錯(cuò)誤,比如返回的json數(shù)據(jù)你卻用了xml解析,還有很多情況等等。

validationError:校驗(yàn) json 錯(cuò)誤,這里包括?[request statusCodeValidator]?和?[request jsonValidator?兩種類型的錯(cuò)誤,前者為返回的 statusCode 不在你指定的成功請(qǐng)求區(qū)間內(nèi),后者為返回的 json 數(shù)據(jù) 跟你重載的 jsonValidator 函數(shù)中存在字段不一致的情況。

[可選] 如果你用 JSONModel 還會(huì)有 JSONModel 解析錯(cuò)誤產(chǎn)生的錯(cuò)誤。

處理方式如下:

//這里暫不考慮JSONModel解析錯(cuò)誤的問(wèn)題

- (void)requestFailedPreprocessor

{

? ? //note:子類如需繼承,必須必須調(diào)用 [super requestFailedPreprocessor];

? ? [super requestFailedPreprocessor];

? ? NSError * error = self.error;

? ? if ([error.domain isEqualToString:AFURLResponseSerializationErrorDomain])

? ? {

? ? ? ? //AFNetworking處理過(guò)的錯(cuò)誤

? ? }else if ([error.domain isEqualToString:YTKRequestValidationErrorDomain])

? ? {

? ? ? ? //猿題庫(kù)處理過(guò)的錯(cuò)誤

? ? }else{

? ? ? ? //系統(tǒng)級(jí)別的domain錯(cuò)誤,無(wú)網(wǎng)絡(luò)等[NSURLErrorDomain]

? ? ? ? //根據(jù)error的code去定義顯示的信息,保證顯示的內(nèi)容可以便捷的控制

? ? }

}

這里還有一種特殊情況,就是服務(wù)端返回的錯(cuò)誤不一定是以?錯(cuò)誤?的方式給你??赡苷?qǐng)求狀態(tài)碼依然是200OK,那么這個(gè)時(shí)候需要重寫(xiě) YTK 提供的成功和失敗的block和重寫(xiě)代理

4>loading動(dòng)畫(huà)以及錯(cuò)誤彈出機(jī)制

YTK自帶了一套插件機(jī)制,用于處理?YTKBaseRequest,YTKBatchRequest,YTKChainRequest?這幾種請(qǐng)求的loading展示機(jī)制,只需要傳入?animatingView?和?animatingText?即可。對(duì)于彈出統(tǒng)一的錯(cuò)誤提示,可以在?ZCBaseRequest?的失敗主線程回調(diào)中進(jìn)行。即:

///? Called on the main thread when request failed.

- (void)requestFailedFilter

{

? ? [super requestFailedFilter];

? ? if (![self isHideErrorToast]) {

? ? ? ? UIWindow * window = [[UIApplication sharedApplication] keyWindow];

? ? ? ? UIViewController * controller = [self findBestViewController:window.rootViewController];

? ? ? ? [WMHUDUntil showFailWithMessage:self.error.localizedDescription toView:controller.view];

? ? }

}

其中?[self isHideErrorToast]?用于表示是否隱藏錯(cuò)誤提示。該方法由具體的子類去實(shí)現(xiàn)。

5>網(wǎng)絡(luò)請(qǐng)求的終止

YTK給出網(wǎng)絡(luò)請(qǐng)求關(guān)閉方案:在dealloc中調(diào)用:

Remove self from request queue and cancel the request.

- (void)stop;

所以,建議每個(gè)網(wǎng)絡(luò)請(qǐng)求都在controller寫(xiě)成全局的變量。

下面展示一下具體某個(gè)請(qǐng)求的代碼:

ZCTYKTestViewController.m

@interface ZCTYKTestViewController ()

@property (nonatomic,strong) ZCMeGetInfoManger * infoManger;

@end

@implementation ZCTYKTestViewController

- (void)viewDidLoad {

? ? [super viewDidLoad];

? ? [self setupData];

}

- (void)setupData

{

? ? self.infoManger = [[ZCMeGetInfoManger alloc] init];

? ? self.infoManger.animatingView = self.view;

}

- (IBAction)buttonAction:(UIButton*)sender

{

? ? [self clearTextView];

? ? ZCGetInfoParam * param = [[ZCGetInfoParam alloc] init];

? ? param.userId = @"0";

? ? param.token = @"222222";

? ? _infoManger.param = param;

? ? [_infoManger startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) {

? ? ? ? NSLog(@"responseJSONObject=%@",_infoManger.responseJSONObject);

? ? ? ? ZCGetInfoModel * infoModel = [[ZCGetInfoModel alloc] initWithDictionary:_infoManger.responseJSONObject error:nil];

? ? ? ? [self updateTextViewWithLog:[NSString stringWithFormat:@"讀取數(shù)據(jù):\n%@",infoModel]];

? ? } failure:^(__kindof YTKBaseRequest * _Nonnull request) {

? ? ? ? [weakself updateTextViewWithLog:[NSString stringWithFormat:@"讀取失敗:\n%@",weakself.infoManger.error]];

? ? }];

}

原文:http://www.360doc.com/content/18/0418/18/54658302_746699386.shtml

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • YTKNetwork介紹 YTKNetwork 是猿題庫(kù) iOS 研發(fā)團(tuán)隊(duì)基于 AFNetworking 封裝的 ...
    天清水藍(lán)閱讀 15,882評(píng)論 37 77
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,614評(píng)論 19 139
  • 寫(xiě)在前面: 這篇文寫(xiě)了什么?用YTKNetwork走一次網(wǎng)絡(luò)請(qǐng)求的完整歷程。 我能看到哪些內(nèi)容?以下是YTKNet...
    RubyAhooo閱讀 4,344評(píng)論 1 10
  • 也許你在人生的某個(gè)階段,某一個(gè)瞬間會(huì)出現(xiàn)陰影!但那是因?yàn)槟惚澈笥嘘?yáng)光,我希望那個(gè)陽(yáng)光是我!你就大踏步地朝前走,不管...
    綠色閃電閱讀 384評(píng)論 1 1
  • 生活過(guò)得不太順利的時(shí)候,寫(xiě)文字總是寫(xiě)得很順利。慢慢地,寫(xiě)字成了我的一個(gè)寄托,不僅是心里苦到不行的時(shí)候可以做的事,也...
    yy日常閱讀 544評(píng)論 0 0

友情鏈接更多精彩內(nèi)容