Swift3.0 + AFN3.0 + MJExtention + (泛型)封裝網(wǎng)絡請求類

寫在前面:對于移動端開發(fā)來說,無論iOS,Android,抑或是H5開發(fā),所謂的開發(fā)核心之一就是從服務端接收數(shù)據(jù),前端負責展示,所以,當然還有各種邏輯的實現(xiàn),但是呢,一個完善的APP肯定是少不了與服務端交互的,所以一個網(wǎng)絡請求類的封裝好用與否直接關系到一個項目的開發(fā)效率以及后期的維護,本人也接手過那種沒有請求類的項目呢,每個請求寫了一堆代碼,用的全部是ASI,,然后需要更改為AFN的時候,哇,簡直無敵了,然后Command+F都用不上,只能一處處找,找的自己都不知道時間了,真的相當折磨,受過這個折磨的童鞋肯定能夠體會這種痛苦,有種刪除項目,拍屁股走人的邪惡想法,但是呢,畢竟咱的職業(yè)素養(yǎng)還在,所以就耐心的享受這折磨了.廢話不多說,相信每個開發(fā)的童鞋都能知道一個好的網(wǎng)絡請求類帶給我們的便捷之處.

** 1.Swift3.0**

不用過多介紹什么,Swift是蘋果主推的開發(fā)語言,老司機都知道原來是OC,Swift語言筆者前天花了一下午+一晚上的時間學習了下,確實很簡潔,開發(fā)速度確實很快,風格類似于(JS+Python)我比較喜歡的是Swift中的泛型,泛型這個東西Java中有,當初寫Java的時候泛型真的讓我方便了很多很多,所以在進行iOS開發(fā)的時候一直琢磨著有沒有泛型這個東西,可是呢,都知道沒有...最近安卓新出的kotlin語言,跟Swift也是大同小異吧.

** 2.AFN**

AFN是一套iOS網(wǎng)絡請求框架,用OC語言編寫(我記得原來有Swift語言編寫的,但是去GitHub上找的時候沒有找到)這個框架現(xiàn)在幾乎所有的iOS項目都在用吧,確實很好用,當然不缺乏大牛公司自己封裝的,當然也可能有老項目還在用ASI(曾經(jīng)火了好長時間,但是現(xiàn)在沒人用了,WHy?因為作者不更新了...可悲不)

** 3.MJExtention**

MJExtention是李明杰大神的一套框架,主要用來字典轉模型,這個框架的好用程度本人給100分,當然還有別的功能,其他的童鞋們慢慢去挖掘吧.即便你的對象里包含對象數(shù)組,它也可以幫你全部轉換的哦,有不懂的或者有興趣的可以直接去GitHub上下載學習一下,或者給我留言也可以哦.

4.封裝思想

我也相信這些封裝思想應該很多人都知道了,思想就像風暴一樣,瞬間可以席卷全球,真是個神奇的東西,當然我的這個也是來源于網(wǎng)絡上大神的分享.
AFN的源碼我看了一遍,我們可以直接用AFHTTPSessionManager,有興趣的童鞋可以研究下AFN的源碼,然后我的想法是繼承AFHTTPSessionManager創(chuàng)建一個RequsetManager類,(當然第一版我用的是裝飾模式,是在RequsetManager中裝飾一個AFHTTPSessionManager對象,當然兩種方法各有各的優(yōu)點,直接繼承可能調用方法上更加簡潔一點,然后也會減少一個對象的使用...)提供一個RequsetManager單例,然后創(chuàng)建一個NetworkService類,為所有的請求提供服務,在這個里面使用包裝RequsetManager,使用RequsetManager下載數(shù)據(jù),在NetworkService中提供對數(shù)據(jù)的解密,緩存(當然我沒有去實現(xiàn)緩存的功能),以及對數(shù)據(jù)的解析轉換(MJExtention在這兒真的起了很大的作用--在此轉換數(shù)據(jù)的時候我們需要知道將json數(shù)據(jù)轉換為什么樣的對象,在OC版本中實現(xiàn)的時候我們是將要轉換對象的Class當做參數(shù)傳了過去,但是返回的結果是id的,我們用的時候需要進行強轉,但是在Swift中我們有了泛型,所以這一步就簡潔了很多,具體的祥看代碼,后面會同樣奉上OC版本的封裝).

5.OC版本

RequsetManager:僅僅實現(xiàn)了Post和GET方法,聲明了成功和失敗的Block回調,一個重新載入請求頭的,當然大部分是沒有用的,另外一個是獲取單利的,但是,所有的方法均是靜態(tài)方法,獲取單利是可以在外部單獨設置請求頭


#import "AFHTTPSessionManager.h"

typedef void (^requestSuccessBlock)(id responseObj);
typedef void (^requestFailureBlock) (NSError *error);

@interface RequsetManager : AFHTTPSessionManager
+ (instancetype)sharedRequestManager;
+ (void)AFN_ReloadHeaderAuth;
+ (void)AFN_GetRequest:(NSString *)url params:(NSDictionary *)params success:(requestSuccessBlock)successHandler failure:(requestFailureBlock)failureHandler;
+ (void)AFN_PostRequest:(NSString *)url params:(NSDictionary *)params success:(requestSuccessBlock)successHandler failure:(requestFailureBlock)failureHandler;
@end

實現(xiàn)如下:

#import "RequsetManager.h"
@interface RequsetManager()
@end
@implementation RequsetManager
+ (instancetype)sharedRequestManager{
    
    static dispatch_once_t onceToken;
    static RequsetManager * manager = nil;
    dispatch_once(&onceToken, ^{
        manager = [[self alloc]init];
        manager.requestSerializer.timeoutInterval = 60.0f;
        [manager.requestSerializer setValue:@"" forHTTPHeaderField:@"jwttoken"];
        manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    });
    return manager;
}

+ (void)AFN_ReloadHeaderAuth{
    [[[self sharedRequestManager] requestSerializer] setValue:@"" forHTTPHeaderField:@"jwttoken"];
}

+ (void)AFN_GetRequest:(NSString *)url params:(NSDictionary *)params success:(requestSuccessBlock)successHandler failure:(requestFailureBlock)failureHandler{
    [[self sharedRequestManager] GET:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        successHandler(responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        failureHandler(error);
    }];
}

+ (void)AFN_PostRequest:(NSString *)url params:(NSDictionary *)params success:(requestSuccessBlock)successHandler failure:(requestFailureBlock)failureHandler{
    [[self sharedRequestManager] POST:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        successHandler(responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        failureHandler(error);
    }];

}

@end

RequestService:僅僅封裝了返回結果為JSON的,其實有XMLDictionary可以將XML轉換為JSON的,有興趣的可以從后面我的GitHub上下載demo ,里面包含了這個類,當然也可以直接從GitHub上搜索

#import <Foundation/Foundation.h>
typedef void (^responseResultBlock)(id dataObj, NSError *error);
@interface RequestService : NSObject

/**
 重新載入請求頭
 */
+ (void)reloadAFNHeaderAuth;

/**
 利用AFN請求獲取JSON返回 --- GET請求

 @param url 請求地址
 @param param 請求參數(shù)
 @param modelClass 請求返回所需要轉換的模型類
 @param responseBlock 請求成功|失敗回調
 */
+ (void)AFN_JSONResponseGetWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock) responseBlock;

/**
 利用AFN請求獲取JSON返回 --- POST請求
 
 @param url 請求地址
 @param param 請求參數(shù)
 @param modelClass 請求返回所需要轉換的模型類
 @param responseBlock 請求成功|失敗回調
 */
+ (void)AFN_JSONResponsePostWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock)responseBlock;


/**
 請求返回結果轉換為模型方法  -- 此方法不需要關注,如果有Service繼承此類,可以重寫該方法進行數(shù)據(jù)處理

 @param responseObj 返回結果
 @param modelClass 模型類
 @return 轉換成功的模型
 */
+ (id)modelTransformationWithResponseObj:(id)responseObj modelClass:(Class)modelClass;

@end

具體實現(xiàn)如下:

#import "RequestService.h"
#import "RequsetManager.h"
#import "MJExtension.h"

static id dataObj;
@implementation RequestService

+ (void)reloadAFNHeaderAuth{
    [RequsetManager AFN_ReloadHeaderAuth];
}
+ (void)AFN_JSONResponseGetWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock) responseBlock{
    [RequsetManager AFN_GetRequest:url params:param success:^(id responseObj) {
        dataObj = [self modelTransformationWithResponseObj:responseObj modelClass:modelClass];
        responseBlock(dataObj,nil);
    } failure:^(NSError *error) {
        responseBlock(nil,error);
    }];
}
+ (void)AFN_JSONResponsePostWithUrl:(NSString *)url param:(id)param modelClass:(Class)modelClass responseBlock:(responseResultBlock)responseBlock{
    [RequsetManager AFN_PostRequest:url params:param success:^(id responseObj) {
        dataObj = [self modelTransformationWithResponseObj:responseObj modelClass:modelClass];
        responseBlock(dataObj,nil);
    } failure:^(NSError *error) {
        responseBlock(nil,error);
    }];
}


+ (id)convertJson:(NSString *)jsonStr
{
    if (!jsonStr) {
        return nil;
    }
    NSError * error;
    return [NSJSONSerialization JSONObjectWithData:[jsonStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableLeaves error:&error];
}


/**
 解密

 @param response 網(wǎng)絡數(shù)據(jù)
 @return 解密結果
 */
+ (NSString *)decreptResponse:(NSString *)response{
    return response;
}

+ (id)modelTransformationWithResponseObj:(id)responseObj modelClass:(Class)modelClass{
    NSString *response = [[NSString alloc] initWithData:(NSData *)responseObj encoding:NSUTF8StringEncoding];
    response = [self decreptResponse:response];
    
    id tmp = [self convertJson:response];
    if ([tmp isKindOfClass:[NSArray class]]) {
        return [modelClass mj_objectArrayWithKeyValuesArray:responseObj];
    }else if([tmp isKindOfClass:[NSDictionary class]]){
        return [modelClass mj_objectWithKeyValues:responseObj];
    }
    return nil;
}

@end

6.Swift版本

import Foundation



/// 請求工具類
class NetworkService{
    
    
    /// Get 請求
    ///
    /// - Parameters:
    ///   - url: 請求地址
    ///   - params: 請求參數(shù)
    ///   - complete: 請求回調 -- 回傳結果為對象
    static func GetRequest<T:NSObject>(url:String,
                           params:[String:AnyObject]?,
                           complete:@escaping (_ dataModel:T?,_ error:Error?)->()){
        
        RequsetManager.getRequest(url: url, params: params, success: { (result) in
            self.transformModel(result: result, complete: { (resultObj:T?,resultArr:[T]?) in
                complete(resultObj,nil)
            })
        }) { (error) in
            complete(nil,error)
        }
    }
    
    
    
    /// Get請求
    ///
    /// - Parameters:
    ///   - url: 請求地址
    ///   - params: 請求參數(shù)
    ///   - complete: 請求完成回調 -- 回傳結果為對象數(shù)組
    static func GetRequest<T:NSObject>(url:String,
                           params:[String:AnyObject]?,
                           complete:@escaping (_ dataModelArray:[T]?,_ error:Error?)->()){
        
        RequsetManager.getRequest(url: url, params: params, success: { (result) in
            self.transformModel(result: result, complete: { (resultObj:T?,resultArr:[T]?) in
                complete(resultArr,nil)
            })
        }) { (error) in
            complete(nil,error)
        }
    }
    
    
    
    
    /// Post 請求
    ///
    /// - Parameters:
    ///   - url: 請求地址
    ///   - params: 請求參數(shù)
    ///   - complete: 請求回調 -- 回傳結果為對象
    static func PostRequest<T:NSObject>(url:String,
                           params:[String:AnyObject]?,
                           complete:@escaping (_ dataModel:T?,_ error:Error?)->()){
        
        RequsetManager.postRequest(url: url, params: params, success: { (result) in
            self.transformModel(result: result, complete: { (resultObj:T?,resultArr:[T]?) in
                complete(resultObj,nil)
            })
        }) { (error) in
            complete(nil,error)
        }
    }
    
    
    
    /// Post請求
    ///
    /// - Parameters:
    ///   - url: 請求地址
    ///   - params: 請求參數(shù)
    ///   - complete: 請求完成回調 -- 回傳結果為對象數(shù)組
    static func PostRequest<T:NSObject>(url:String,
                           params:[String:AnyObject]?,
                           complete:@escaping (_ dataModelArray:[T]?,_ error:Error?)->()){
        
        RequsetManager.postRequest(url: url, params: params, success: { (result) in
            self.transformModel(result: result, complete: { (resultObj:T?,resultArr:[T]?) in
                complete(resultArr,nil)
            })
        }) { (error) in
            complete(nil,error)
        }
    }
    
    
    
    
    
    
    
    /// 轉換方法
    ///
    /// - Parameters:
    ///   - result: 網(wǎng)絡返回數(shù)據(jù)
    ///   - complete: 轉換完成回調
    static func transformModel<T:NSObject>(result:Any?,
                               complete:@escaping (_ result:T?,_ resultArray:[T]?)->()){
        
        
        let jsonStr = self.converNetData(result: result as Any)
        let obj = T.mj_object(withKeyValues: jsonStr)
        var objArr = [T]()
        if T.mj_objectArray(withKeyValuesArray: jsonStr) != nil {
            for item in T.mj_objectArray(withKeyValuesArray: jsonStr) {
                objArr.append(item as! T)
            }
        }
        complete(obj,objArr)
    }
    

    
    
    /// 轉換網(wǎng)絡數(shù)據(jù)
    ///
    /// - Parameter result: 網(wǎng)絡數(shù)據(jù)
    /// - Returns: 返回結果
    static func converNetData(result:Any) -> String{
        let respose = String.init(data: result as! Data, encoding: String.Encoding.utf8)
        return self.decreptRespose(respose: respose)

    }
    
    
    /// 解密
    ///
    /// - Parameter respose: 網(wǎng)絡數(shù)據(jù)
    /// - Returns: 解密結果
    static func decreptRespose(respose:String?) -> String{
        
        //自己實現(xiàn)解密
        return respose ?? "{}"
    }
}




/// 請求管理
class RequsetManager :AFHTTPSessionManager {
    
    
    /// 初始化RequsetManager單利
    static let sharedRequestManager:RequsetManager = {
        let instance = RequsetManager()
        instance.requestSerializer.timeoutInterval = 60.0
        //自定義設置請求頭
//        instance.requestSerializer.setValue(<#T##value: String?##String?#>, forHTTPHeaderField: <#T##String#>)
        instance.responseSerializer = AFHTTPResponseSerializer()
        return instance
    }()

    
    
    /// GET請求
    ///
    /// - Parameters:
    ///   - url: 請求地址
    ///   - params: 請求參數(shù)
    ///   - success: 請求成功回調
    ///   - failure: 請求失敗回調
    static func getRequest(url:String,
                    params:[String:AnyObject]?,
                    success:@escaping (_ responseData:Any?)->()?,
                    failure:@escaping (_ error:Error)->()?){
        
        RequsetManager.sharedRequestManager.get(url, parameters: params, progress: nil, success: {
            (task:URLSessionDataTask,result:Any?) in
            success(result)
            
        }) { (task:URLSessionDataTask?, error:Error) in
            failure(error)
        }
    }
    
    
    
    /// POST請求
    ///
    /// - Parameters:
    ///   - url: 請求地址
    ///   - params: 請求參數(shù)
    ///   - success: 請求成功回調
    ///   - failure: 請求失敗回調
    static func postRequest(url:String,
                    params:[String:AnyObject]?,
                    success:@escaping (_ responseData:Any?)->()?,
                    failure:@escaping (_ error:Error)->()?){
        RequsetManager.sharedRequestManager.post(url, parameters: params, progress: nil, success: {
            (task:URLSessionDataTask,result:Any?) in
            success(result)
            
        }) { (task:URLSessionDataTask?, error:Error) in
            failure(error)
        }
    }
}

7.對比--使用--分析

Swift版本其實就是對OC版本的一個更改在RequsetManager層次幾乎是一樣的,在Service層,OC版本需要在使用的時候將所要轉換的對象的Class當做參數(shù)傳過來,而在Swift版本中,僅僅需要在閉包中用泛型就可以,無需再需要傳遞對象的類
在使用上,OC版本回調回去的是一個id對象,無論是對象數(shù)組抑或是對象,都需要強制轉換后使用,而在Swift版本中,我用了多態(tài)的性質,根據(jù)使用時傳遞的閉包不同,返回的結果也不同,結果是對象的和結果是對象數(shù)組的,可以分別選擇調用相對應的方法.

使用
返回結果是對象的OC版本:

- (void)test1{
    [RequestService AFN_JSONResponseGetWithUrl:@"http://192.168.1.107:8080/SkeeterTask/test/login" param:nil modelClass:[TestModel class] responseBlock:^(id dataObj, NSError *error) {
        if (error) {
            return ;
        }
        //使用上需要將id類型的dataObj進行強轉...
        NSLog(@"結果為對象---%@",((TestModel *)dataObj).message);
    }];
}

返回結果是對象數(shù)組的OC版本:

[RequestService AFN_JSONResponseGetWithUrl:@"http://192.168.1.107:8080/SkeeterTask/test/login2" param:nil modelClass:[TestModel class] responseBlock:^(id dataObj, NSError *error) {
        if (error) {
            return ;
        }
        
        //此時沒必要強制轉換,如果網(wǎng)絡數(shù)據(jù)返回的是數(shù)組,得到的dataObj肯定是數(shù)組
        而且里面的對象肯定是傳的modelClass對象
        for (TestModel * mode in dataObj) {
            NSLog(@"結果為對象數(shù)組--%@",mode.message);
        }
    }];

返回結果是對象的Swift版本

NetworkService .GetRequest(url: "http://192.168.1.107:8080/SkeeterTask/test/login", params: nil) { (model:TestModel?, error:Error?) in
            if error != nil {
                print("Error")
                return
            }
            
            //調用方法的時候TestModel是當做泛型傳遞過去的,所以回調的model
            是可以直接來使用的
            print("結果為對象--->\(model?.message ?? "NoMessage")")
        }

返回結果是對象數(shù)組的Swift版本

NetworkService.GetRequest(url: "http://192.168.1.107:8080/SkeeterTask/test/login2", params: nil) { (modelArr:[TestModel]?, error:Error?) in
            if error != nil{
                print("Error")
                return
            }
            
            //運用傳遞的閉包不同,返回的modelArr的泛型為TestModel
            所以返回過來之后可以直接使用了
            if let arr = modelArr{
                for item in arr{
                    print("結果為對象數(shù)組--->\(item.message ?? "NoMessage")")
                }
            }
        }

分析,這兩中其實思想是一樣的,只不過Swift版本因為使用了泛型,所以在使用的時候更加清晰,而且也不需要將所要轉換的對象類當做參數(shù)進行傳遞,而且使用了多態(tài),根據(jù)返回的結果不同分別傳遞不同的閉包,而OC版本,始終都是那一個方法,需要對返回的結果進行強制轉換,所以使用起來沒有那么的一目了然,大致就是這樣,希望我這個可以拋磚引玉,有好的想法的童鞋可以給我留言交流,共同提高

8.寫在最后

最后提醒一下,我請求的地址是我使用Java寫的本地的一個服務,小伙伴們在使用的時候可要進行相對應的更改哦,最后奉上我測試的結果截圖,有什么不足呢,可以給我留言溝通共同提高哦.謝謝

Snip20170610_1.png
Snip20170610_2.png

10.最后的最后,奉上完整的Demo地址(XML轉JSON的類在OC源碼中)

OC版本源碼

Swift版本源碼

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末劲件,一起剝皮案震驚了整個濱河市殴蓬,隨后出現(xiàn)的幾起案子鳍征,更是在濱河造成了極大的恐慌欣尼,老刑警劉巖雌团,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異椿猎,居然都是意外死亡危尿,警方通過查閱死者的電腦和手機食棕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門朗和,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人簿晓,你說我怎么就攤上這事眶拉。” “怎么了憔儿?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵镀层,是天一觀的道長。 經(jīng)常有香客問我皿曲,道長,這世上最難降的妖魔是什么吴侦? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任屋休,我火速辦了婚禮,結果婚禮上备韧,老公的妹妹穿的比我還像新娘劫樟。我一直安慰自己,他們只是感情好织堂,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布叠艳。 她就那樣靜靜地躺著,像睡著了一般易阳。 火紅的嫁衣襯著肌膚如雪附较。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天潦俺,我揣著相機與錄音拒课,去河邊找鬼。 笑死事示,一個胖子當著我的面吹牛早像,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肖爵,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼卢鹦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了劝堪?” 一聲冷哼從身側響起冀自,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤揉稚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后凡纳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窃植,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年荐糜,在試婚紗的時候發(fā)現(xiàn)自己被綠了巷怜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡暴氏,死狀恐怖延塑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情答渔,我是刑警寧澤关带,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站沼撕,受9級特大地震影響宋雏,放射性物質發(fā)生泄漏。R本人自食惡果不足惜务豺,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一磨总、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧笼沥,春花似錦蚪燕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至汹桦,卻和暖如春鲁驶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背舞骆。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工灵嫌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人葛作。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓寿羞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赂蠢。 傳聞我的和親對象是個殘疾皇子绪穆,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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

  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,033評論 4 62
  • 一直說細節(jié)決定成敗玖院,因我本身也是馬大哈的那種菠红,對提到的細節(jié)類一向本能抵觸也不放在心上。在工作上反映出來难菌,不是交到人...
    龍少之說閱讀 469評論 5 11
  • 1.StarCitizen星際公民3.0菜單试溯、鍵位圖文漢化 http://www.reibang.com/p/a...
    星際公民閱讀 3,376評論 0 2
  • 我一直認為,寫東西就是把心中所思所想用文字這個媒介表達出來郊酒,所以我一直都是遇到什么事或者突然產(chǎn)生什么想法就寫下來...
    夜吟月光閱讀 1,423評論 4 51