AFNetworking源碼分析

一丧靡、概述

AFNetworking代碼結(jié)構(gòu)

從上圖可以發(fā)現(xiàn),AFNetworking框架的結(jié)構(gòu)比較清晰簡(jiǎn)潔钞支,主要范圍5個(gè)部分:

  • 網(wǎng)絡(luò)通信模塊:URLSession
  • 網(wǎng)絡(luò)狀態(tài)監(jiān)聽模塊:Reachability
  • 網(wǎng)絡(luò)通信安全模塊:Security
  • 網(wǎng)絡(luò)通信序列化模塊:Serialization
  • 對(duì)UIKit框架擴(kuò)展部分:UIKit(以Catagory形式添加特性)
    AFNetworking的核心是AFURLSessionManager類绅作,這個(gè)類基于NSURLSession营曼,圍繞NSURLSession做了一系列的封裝,其余的四個(gè)類都是該類用于網(wǎng)絡(luò)通信的一個(gè)屬性或?qū)σ延蠻IKit的一個(gè)擴(kuò)展工具包尿贫。
    其中AFHTTPSessionManager類是繼承于AFURLSessionManager的电媳,我們使用AFNetworking時(shí),都是用AFHTTPSessionManager庆亡,但它本身沒有做實(shí)事的匾乓,只是做了一些簡(jiǎn)單的封裝,把請(qǐng)求邏輯分發(fā)給父類AFURLSessionManager又谋。

二拼缝、AFNetworking使用

例子:

AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
session.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json", @"text/json" ,@"text/javascript", nil];
session.responseSerializer = [AFHTTPResponseSerializer serializer];
[session GET:@"https://www.baidu.com"
  parameters:nil
     headers:nil
    progress:nil
     success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
         NSLog(@"請(qǐng)求成功");
     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
          NSLog(@"請(qǐng)求失敗");
     }];

三、AFNetworking使用的代碼分析

3.1 [AFHTTPSessionManager manager]

AFHTTPSessionManager is a subclass of AFURLSessionManager with convenience methods for making HTTP requests. When a base URL is provided, requests made with the GET/POST/ et al. convenience methods can be made with relative paths.

AFHTTPSessionManager manage的調(diào)用棧

AFHTTPSessionManager manage的實(shí)現(xiàn)

+ (instancetype)manager {
    return [[[self class] alloc] initWithBaseURL:nil];
}

- (instancetype)init {
    return [self initWithBaseURL:nil];
}

- (instancetype)initWithBaseURL:(NSURL *)url {
    return [self initWithBaseURL:url sessionConfiguration:nil];
}

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    return [self initWithBaseURL:nil sessionConfiguration:configuration];
}

- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }
    
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }
    
    self.baseURL = url;
    
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    
    return self;
}

進(jìn)入到AFURLSessionManager- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration方法中分析

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
// 1. 設(shè)置全局的網(wǎng)絡(luò)行為策略的配置
    self.sessionConfiguration = configuration;
// 2. 設(shè)置請(qǐng)求的隊(duì)列彰亥,默認(rèn)最大的并發(fā)數(shù)為1
    self.operationQueue = [[NSOperationQueue alloc] init];
// (1)這里的并發(fā)數(shù)指的是回調(diào)代理的線程并發(fā)數(shù)咧七,而不是請(qǐng)求網(wǎng)絡(luò)的線程并發(fā)數(shù)。請(qǐng)求網(wǎng)絡(luò)是由NSURLSession來做的任斋,它內(nèi)部維護(hù)了一個(gè)線程池用來做網(wǎng)絡(luò)請(qǐng)求猪叙。NSURLSession調(diào)度線程是基于底層的CFSocket去發(fā)送請(qǐng)求和接收數(shù)據(jù),這些線程是并發(fā)的。
// (2)AF2.x所有回調(diào)是在一條線程穴翩,這條線程是AF的常駐線程犬第,而這一條線程正式AF調(diào)度request的思想精髓所在。所以線程數(shù)設(shè)置為1的第一個(gè)目的是和之前版本保持一致芒帕;
// (3)因?yàn)楦硐嚓P(guān)的一些操作AF都使用了NSLock歉嗓,所以Queue的并發(fā)數(shù)設(shè)置為n,也會(huì)因?yàn)槎嗑€程回調(diào)背蟆、鎖的等待鉴分,導(dǎo)致提升的程序速度并不明顯,反而多task回調(diào)導(dǎo)致的多線程并發(fā)带膀,平白浪費(fèi)了部分性能志珍。至少回調(diào)的事件,是不需要多線程并發(fā)的垛叨,回調(diào)沒有了NSLock的等待事件伦糯,所以對(duì)事件并沒有多大影響。
    self.operationQueue.maxConcurrentOperationCount = 1;
// 3. 設(shè)置網(wǎng)絡(luò)請(qǐng)求響應(yīng)的數(shù)據(jù)解析實(shí)例
    self.responseSerializer = [AFJSONResponseSerializer serializer];
// 4. 設(shè)置網(wǎng)絡(luò)請(qǐng)求安全策略實(shí)例(后續(xù)說明該實(shí)例)
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];
// 5. 初始化全局的網(wǎng)絡(luò)狀態(tài)監(jiān)聽的實(shí)例
#if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
// 6. 將taskID與其Delegate綁定嗽元,實(shí)現(xiàn)解耦(后續(xù)進(jìn)行分析)
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
// 7. self.session采用懶加載敛纲,根據(jù)configuration,operationQueue初始化全局的NSURLSession
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }

        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }

        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];

    return self;
}

- (NSURLSession *)session {
    
    @synchronized (self) {
        if (!_session) {
            _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
        }
    }
    return _session;
}

3.2 NSURLSessionConfiguration 全局的網(wǎng)絡(luò)行為策略的配置

An NSURLSessionConfiguration object defines the behavior and policies to use when uploading and downloading data using an NSURLSession object. When uploading or downloading data, creating a configuration object is always the first step you must take. You use this object to configure the timeout values, caching policies, connection requirements, and other types of information that you intend to use with your NSURLSession object.

簡(jiǎn)單羅列以下兩點(diǎn):
1.NSURLSessionConfiguration可以控制網(wǎng)絡(luò)請(qǐng)求中的緩存策略剂癌,超時(shí)設(shè)置等淤翔;
2.如果需要改變網(wǎng)絡(luò)請(qǐng)求的行為策略,必須重新在更改NSURLSessionConfiguration后再創(chuàng)建一個(gè)新的NSURLSession佩谷。

3.3 AFHTTPSessionManager GET: parameters: headers: progress: success: failure: 方法

AFHTTPSessionManager GET: parameters: headers: progress: success: failure: 方法調(diào)用棧

3.3.1 NSURLSessionTask

這里引入了一個(gè)新的類:NSURLSessionDataTask旁壮。NSURLSessionDataTask繼承了NSURLSessionTask。** NSURLSessionTask**的官方說明:

The NSURLSessionTask class is the base class for tasks in a URL session. Tasks are always part of a session; you create a task by calling one of the task creation methods on an NSURLSession object. The method you call determines the type of task.
URL sessions provide three types of tasks: data tasks, upload tasks, and download tasks. These tasks are instances of the NSURLSessionDataTask, NSURLSessionUploadTask, NSURLSessionDownloadTask, NSURLSessionStreamTask subclasses of NSURLSessionTask, respectively.

簡(jiǎn)單羅列以下兩點(diǎn):
1.NSURLSessionTask是官方提供的幾種網(wǎng)絡(luò)任務(wù)類的基類谐檀;
2.官方提供了三種任務(wù)處理的子類:簡(jiǎn)單數(shù)據(jù)處理任務(wù)類寡具、上傳任務(wù)類和下載任務(wù)類。

3.3.2 AFHTTPSessionManager dataTaskWithHTTPMethod:...方法實(shí)現(xiàn)與分析

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                         headers:(NSDictionary<NSString *,NSString *> *)headers
                                  uploadProgress:(void (^)(NSProgress * _Nonnull))uploadProgress
                                downloadProgress:(void (^)(NSProgress * _Nonnull))downloadProgress
                                         success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                                         failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure {
    NSError *serializationError = nil;
// 1.通過全局配置的requestSerializer初始化一個(gè)請(qǐng)求的實(shí)例
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];

    for (NSString *headerField in headers.keyEnumerator) {
        [request setValue:headers[headerField] forKey:headerField];
    }
    
    if (serializationError) {
        if (failure) {
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }
        return nil;
    }
    
    // 2.根據(jù)請(qǐng)求的實(shí)例再初始化一個(gè)task的實(shí)例
    __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;
}

3.3.3 [AFURLSessionManager addDelegateForDataTask:...]方法稚补,用于task與delegate的綁定

// 1. 根據(jù)請(qǐng)求信息初始化一個(gè)task實(shí)例
- (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 {

    NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];

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

    return dataTask;
}


- (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
{
// 2.初始化一個(gè)任務(wù)的代理
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;

// 此處賦值了請(qǐng)求完成的回調(diào)童叠,后續(xù)會(huì)用到
    delegate.completionHandler = completionHandler;
// 3.利用全局的字典存儲(chǔ)綁定信息,key為taskID课幕,value為代理的實(shí)例厦坛。
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

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

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
// 1.綁定taskID與代理self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 2.使用KVO對(duì)一些方法監(jiān)聽,返回上傳或下載的進(jìn)度
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

3.3.4 NSURLSessionTaskDelegate協(xié)議中的URLSession:task:didCompleteWithError:方法

NSURLSessionTaskDelegate協(xié)議包含NSURLSessionDelegate協(xié)議
當(dāng)請(qǐng)求收到了響應(yīng)后乍惊,會(huì)觸發(fā)- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error;回調(diào)(AFURLSessionManager的session屬性懶加載時(shí)杜秸,設(shè)置了delegate為self,會(huì)觸發(fā)該代理的方法)润绎,以下是方法中的具體處理撬碟。

// AFURLSessionManager.m中NSURLSessionTaskDelegate方法的實(shí)現(xiàn)诞挨,該方法由系統(tǒng)自動(dòng)觸發(fā)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
// 1.根據(jù)task獲取綁定的代理實(shí)例
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // delegate may be nil when completing a task in the background
    if (delegate) {
// 2.統(tǒng)一處理,調(diào)用AFURLSessionManagerTaskDelegate的同名方法呢蛤。
        [delegate URLSession:session task:task didCompleteWithError:error];

        [self removeDelegateForTask:task];
    }

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

// AFURLSessionManagerTaskDelegate.m中NSURLSessionTaskDelegate方法的實(shí)現(xiàn)惶傻,該方法在AFURLSessionManager中通過delegate調(diào)用。
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    error = objc_getAssociatedObject(task, AuthenticationChallengeErrorKey) ?: error;
    __strong AFURLSessionManager *manager = self.manager;

    __block id responseObject = nil;

    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 AF_CAN_USE_AT_AVAILABLE && AF_CAN_INCLUDE_SESSION_TASK_METRICS
    if (@available(iOS 10, macOS 10.12, watchOS 3, tvOS 10, *)) {
        if (self.sessionTaskMetrics) {
            userInfo[AFNetworkingTaskDidCompleteSessionTaskMetrics] = self.sessionTaskMetrics;
        }
    }
#endif

    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    if (error) {
// 1.iOS網(wǎng)絡(luò)框架返回的錯(cuò)誤信息處理
        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 {
// 2.請(qǐng)求成功后需要用響應(yīng)的數(shù)據(jù)解析類的responseSerializer實(shí)例處理返回的數(shù)據(jù)
// 數(shù)據(jù)通過NSURLSessionDataDelegate協(xié)議的- (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data方法返回
        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(), ^{
// 3.綁定task與處理的數(shù)據(jù)代理時(shí)其障,傳入代理的完成的回調(diào)
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }

                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
}

參考來源:
https://toutiao.io/posts/dibcw7/preview
https://www.cnblogs.com/ederwin/articles/10592839.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末银室,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子励翼,更是在濱河造成了極大的恐慌蜈敢,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汽抚,死亡現(xiàn)場(chǎng)離奇詭異抓狭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)造烁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門否过,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人膨蛮,你說我怎么就攤上這事〖狙校” “怎么了敞葛?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)与涡。 經(jīng)常有香客問我惹谐,道長(zhǎng),這世上最難降的妖魔是什么驼卖? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任氨肌,我火速辦了婚禮,結(jié)果婚禮上酌畜,老公的妹妹穿的比我還像新娘怎囚。我一直安慰自己,他們只是感情好桥胞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布恳守。 她就那樣靜靜地躺著,像睡著了一般贩虾。 火紅的嫁衣襯著肌膚如雪催烘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天缎罢,我揣著相機(jī)與錄音伊群,去河邊找鬼考杉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛舰始,可吹牛的內(nèi)容都是我干的崇棠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蔽午,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼易茬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起及老,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤抽莱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后骄恶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體食铐,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年僧鲁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虐呻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寞秃,死狀恐怖斟叼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情春寿,我是刑警寧澤朗涩,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站绑改,受9級(jí)特大地震影響谢床,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厘线,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一识腿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧造壮,春花似錦渡讼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至楞抡,卻和暖如春伟众,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背召廷。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工凳厢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留账胧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓先紫,卻偏偏與公主長(zhǎng)得像治泥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子遮精,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

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