一問一答看SDWebImage源碼

怎樣進(jìn)行圖片下載炸宵?

圖片下載真正的動(dòng)作是在這里

//SDWebImageDownloader.m
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
    __block SDWebImageDownloaderOperation *operation;
    __weak __typeof(self)wself = self;
    //addProgressCallback...方法主要是將progressBlock(過程回調(diào))和completedBlock(結(jié)束回調(diào))保存起來爽撒。
    //以u(píng)rl為key保存到SDWebImageDownloader的URLCallbacks里面,供后面使用。
    [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^{
    NSTimeInterval timeoutInterval = wself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }

        // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
        request.HTTPShouldUsePipelining = YES;
        if (wself.headersFilter) {
            request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
        }
        else {
            request.allHTTPHeaderFields = wself.HTTPHeaders;
        }
        operation = [[wself.operationClass alloc] initWithRequest:request
                                                        inSession:self.session
                                                          options:options
                                                         progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                                                          //過程回調(diào)
                                                         }
                                                        completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                                                          //結(jié)束回調(diào)
                                                         }
                                                        cancelled:^{
                                                          //取消回調(diào)
                                                        }
                    ];
        operation.shouldDecompressImages = wself.shouldDecompressImages;
        
        if (wself.urlCredential) {
            operation.credential = wself.urlCredential;
        } else if (wself.username && wself.password) {
            operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
        }
        
        if (options & SDWebImageDownloaderHighPriority) {
            operation.queuePriority = NSOperationQueuePriorityHigh;
        } else if (options & SDWebImageDownloaderLowPriority) {
            operation.queuePriority = NSOperationQueuePriorityLow;
        }

        [wself.downloadQueue addOperation:operation];
        if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
            // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
            [wself.lastAddedOperation addDependency:operation];
            wself.lastAddedOperation = operation;
        }
    }];
    return operation;
}

從代碼可以看出移怯,先是創(chuàng)建了一個(gè)SDWebImageDownloaderOperation的實(shí)例operation,然后把它添加到下載隊(duì)列downloadQueue中这难。SDWebImageDownloaderOperation繼承于NSOperation舟误,重寫了start方法,downloadQueue在添加后會(huì)盡快開始執(zhí)行姻乓,去調(diào)start方法嵌溢。(NSOperation介紹
緊接著SD下載圖片是通過NSURLSession和NSURLSessionTask配合來完成的。NSURLSession是在SDWebImageDownloader里面實(shí)例化的蹋岩,然后傳入給SDWebImageDownloaderOperation赖草,作為它的一個(gè)屬性,叫session剪个。然后session用來實(shí)例化dataTask秧骑。(為了防止到這里session還沒初始化,所以在里面又做了一層判斷扣囊,如果還沒初始化腿堤,那么我就初始化一個(gè),并且跟外面?zhèn)鬟M(jìn)來的區(qū)分開如暖,用完后也由我自己釋放笆檀。)之后啟動(dòng)任務(wù)開始下載圖片。(NSURLSession介紹

//代碼節(jié)選自SDWebImageDownloaderOperation.m
// This is weak because it is injected by whoever manages this session. If this gets nil-ed out, we won't be able to run
// the task associated with this operation
@property (weak, nonatomic) NSURLSession *unownedSession;
// This is set if we're using not using an injected NSURLSession. We're responsible of invalidating this one
@property (strong, nonatomic) NSURLSession *ownedSession;

- (void)start {
//.......
        NSURLSession *session = self.unownedSession;
        if (!self.unownedSession) {
            NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
            sessionConfig.timeoutIntervalForRequest = 15;
            self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
                                                              delegate:self
                                                         delegateQueue:nil];
            session = self.ownedSession;
        }
        self.dataTask = [session dataTaskWithRequest:self.request];
//.......
        [self.dataTask resume];
//.......
}

- (void)reset {
    self.cancelBlock = nil;
    self.completedBlock = nil;
    self.progressBlock = nil;
    self.dataTask = nil;
    self.imageData = nil;
    self.thread = nil;
    if (self.ownedSession) {
        [self.ownedSession invalidateAndCancel];
        self.ownedSession = nil;
    }
}

那如何取消圖片下載呢盒至?

從上文看酗洒,取消下載圖片的線程只要調(diào)用SDWebImageDownloader- (void)cancelAllDownloads方法,這個(gè)方法會(huì)調(diào)用downloadQueuecancelAllOperations方法取消枷遂。

SDWebImageManager的runningOperations干嘛用的樱衷?

runningOperations是一個(gè)數(shù)組,里面的元素是SDWebImageCombinedOperation酒唉,這個(gè)類直接繼承NSObject矩桂,實(shí)現(xiàn)了SDWebImageOperation(只是為了擁有一個(gè)cancel方法)。

@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>

@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock;
@property (strong, nonatomic) NSOperation *cacheOperation;

@end

runningOperations用于標(biāo)示應(yīng)用目前有多少個(gè)正在獲取圖片的操作(記住痪伦,不是NSOperation侄榴,也不是下載圖片這個(gè)動(dòng)作本身)。當(dāng)用戶所有正在獲取圖片的操作都不想要了的情況网沾,可以調(diào)用- (void)cancelAll方法癞蚕,這個(gè)方法會(huì)對(duì)runningOperations里面的子元素都執(zhí)行cancel方法,之后清空這個(gè)數(shù)組辉哥。

//SDWebImageCombinedOperation
- (void)cancel {
    self.cancelled = YES;
    if (self.cacheOperation) {
        [self.cacheOperation cancel];
        self.cacheOperation = nil;
    }
    if (self.cancelBlock) {
        self.cancelBlock();
        
        // TODO: this is a temporary fix to #809.
        // Until we can figure the exact cause of the crash, going with the ivar instead of the setter
//        self.cancelBlock = nil;
        _cancelBlock = nil;
    }
}

什么桦山?這里也有個(gè)取消攒射?那跟上面講的取消圖片下載有什么關(guān)系?

我們看下面代碼

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
  //...
  __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
  __weak SDWebImageCombinedOperation *weakOperation = operation;
  //....
  @synchronized (self.runningOperations) {
    [self.runningOperations addObject:operation];
  }
  //...
  operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
    //...
  id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {//....}];
  operation.cancelBlock = ^{
    [subOperation cancel];
    
    @synchronized (self.runningOperations) {
        __strong __typeof(weakOperation) strongOperation = weakOperation;
        if (strongOperation) {
            [self.runningOperations removeObject:strongOperation];
        }
    }
  };
  //....

  return operation;
}

operation.cacheOperation其實(shí)是獲取緩存的一個(gè)NSOperation恒水,確切的說應(yīng)該是從磁盤獲取圖片会放,但是在這里并沒有像圖片下載使用到的一樣,這里只是作為一個(gè)是否取消的標(biāo)示而已钉凌。當(dāng)調(diào)用- (void)cancelAll方法時(shí)咧最,operation.cacheOperation的取消是為了取消緩存圖片的獲取。
但是注意到這里還有一個(gè)operation.cancelBlock甩骏。調(diào)用- (void)cancelAll方法是會(huì)執(zhí)行cancelBlock的窗市。那這個(gè)是干嘛的。
上面已經(jīng)講到饮笛,下載圖片的operation是由SDWebImageDownloader- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock方法返回的咨察,返回的operation恰好是給operation.cancelBlock里面調(diào)用。cancelBlock執(zhí)行時(shí)福青,就直接把下載圖片的operation給取消了摄狱。
所以SDWebImageManager調(diào)用- (void)cancelAll后,1會(huì)取消從磁盤加載緩存圖片无午,2會(huì)取消圖片下載動(dòng)作媒役。

緩存如何搜索到?

緩存搜索是在SDImageCache- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock方法中進(jìn)行的宪迟,我們來看下代碼

- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
    if (!doneBlock) {
        return nil;
    }

    if (!key) {
        doneBlock(nil, SDImageCacheTypeNone);
        return nil;
    }

    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        doneBlock(image, SDImageCacheTypeMemory);
        return nil;
    }

    NSOperation *operation = [NSOperation new];
    dispatch_async(self.ioQueue, ^{
        if (operation.isCancelled) {
            return;
        }

        @autoreleasepool {
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage && self.shouldCacheImagesInMemory) {
                NSUInteger cost = SDCacheCostForImage(diskImage);
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                doneBlock(diskImage, SDImageCacheTypeDisk);
            });
        }
    });

    return operation;
}

從內(nèi)存獲取緩存是指從系統(tǒng)提供的NSCache的一個(gè)對(duì)象memCache獲取酣衷。內(nèi)存緩存這里使用的不是集合,我想應(yīng)該是NSCache可以設(shè)置totalCostLimitcountLimit次泽,這兩個(gè)屬性可以在內(nèi)存管理更加自動(dòng)化些穿仪。(NSCache介紹
從磁盤獲取緩存的時(shí)候是先用NSOperation實(shí)例化了一個(gè)operation對(duì)象,operation傳給外面意荤,用于控制磁盤獲取是否取消啊片。磁盤取到對(duì)象后會(huì)根據(jù)緩存策略決定是否將圖片保存到內(nèi)存中。

如何從磁盤找到緩存的圖片玖像?

看下面這個(gè)方法

- (UIImage *)diskImageForKey:(NSString *)key {
    NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
    if (data) {
        UIImage *image = [UIImage sd_imageWithData:data];
        image = [self scaledImageForKey:key image:image];
        if (self.shouldDecompressImages) {
            image = [UIImage decodedImageWithImage:image];
        }
        return image;
    }
    else {
        return nil;
    }
}

上面方法獲取到data之后紫谷,轉(zhuǎn)為image,并根據(jù)是否解壓設(shè)置image捐寥。重點(diǎn)的還是下面這個(gè)方法

- (NSData *)diskImageDataBySearchingAllPathsForKey:(NSString *)key {
    NSString *defaultPath = [self defaultCachePathForKey:key];
    NSData *data = [NSData dataWithContentsOfFile:defaultPath];
    if (data) {
        return data;
    }

    // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
    // checking the key with and without the extension
    data = [NSData dataWithContentsOfFile:[defaultPath stringByDeletingPathExtension]];
    if (data) {
        return data;
    }

    NSArray *customPaths = [self.customPaths copy];
    for (NSString *path in customPaths) {
        NSString *filePath = [self cachePathForKey:key inPath:path];
        NSData *imageData = [NSData dataWithContentsOfFile:filePath];
        if (imageData) {
            return imageData;
        }

        // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
        // checking the key with and without the extension
        imageData = [NSData dataWithContentsOfFile:[filePath stringByDeletingPathExtension]];
        if (imageData) {
            return imageData;
        }
    }

    return nil;
}

從方法名就可以知道是查找所有的路徑笤昨,找到data后返回。這些路徑包括defaultPathcustomPaths上真。defaultPath可以自定義設(shè)置咬腋,如果沒有系統(tǒng)會(huì)默認(rèn)創(chuàng)建。值得一提的是為了確保data的唯一性睡互,SD使用CC_MD5的方式對(duì)key做了加密,然后用來作為文件的名字。

為毛可以控制到圖片的下載進(jìn)度呢就珠?

上文提到寇壳,SD下載圖片是通過NSURLSessionNSURLSessionTask配合來完成的。進(jìn)度的控制歸功于以下的方法

//response帶有目標(biāo)文件的大小妻怎,可以從這個(gè)里面獲取到壳炎。
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
//只要接收到數(shù)據(jù),就會(huì)調(diào)用這個(gè)方法逼侦,所以這個(gè)方法是重復(fù)調(diào)用的匿辩。
//可以從這里獲取到單次接收了多少,然后保存到內(nèi)存變量imageData榛丢,這樣就知道已經(jīng)接收到了多少铲球。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;

有了總量和單次接收量自然可以知道進(jìn)度,這時(shí)候再調(diào)用回調(diào)處理就可以了晰赞。

SD的內(nèi)存和性能處理稼病?

內(nèi)存:進(jìn)入后臺(tái)時(shí)/程序退出(UIApplicationWillTerminateNotification),會(huì)對(duì)過期圖片(什么是過期圖片掖鱼?就是已經(jīng)緩存超過maxCacheAge時(shí)間的那些圖片)進(jìn)行刪除然走。刪除之后如果剩下圖片占有的大小大于最大大小(maxCacheSize)的一半戏挡,那么會(huì)根據(jù)圖片修改時(shí)間排序后芍瑞,刪除舊圖片,直到大小滿足褐墅。當(dāng)程序收到內(nèi)存報(bào)警時(shí)拆檬,將內(nèi)存都刪掉。詳細(xì)代碼見- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock方法掌栅。
性能:圖片的下載都是開新的異步線程秩仆。

//來源SDImageCache.m
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(clearMemory)
                                             name:UIApplicationDidReceiveMemoryWarningNotification
                                           object:nil];
//下面兩個(gè)方法都會(huì)調(diào)用
//- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock方法
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(cleanDisk)
                                             name:UIApplicationWillTerminateNotification
                                           object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(backgroundCleanDisk)
                                             name:UIApplicationDidEnterBackgroundNotification
                                           object:nil];

聽說下載失敗后可以重新下載?

SD是有一個(gè)SDWebImageOptionsSDWebImageRetryFailed猾封,但是這不意味著失敗后會(huì)自己去重新嘗試下載澄耍。
SDWebImageManager有一個(gè)數(shù)組failedURLs,用于存放所有下載圖片失敗的url晌缘,當(dāng)加載圖片的時(shí)候遇到上次失敗過的url并且options沒有設(shè)置為SDWebImageRetryFailed是直接不做處理的齐莲,反之才會(huì)根據(jù)失敗的url重新加載。

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
    //....
    BOOL isFailedUrl = NO;
    @synchronized (self.failedURLs) {
        isFailedUrl = [self.failedURLs containsObject:url];
    }

    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        dispatch_main_sync_safe(^{
            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
            completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
        });
        return operation;
    }
    //....
}

聽說SD里面圖片的唯一標(biāo)示可以自定義磷箕?

SDWebImageManager提供了一個(gè)cacheKeyFilter供程序員設(shè)置选酗,之后在要對(duì)圖片操作前,會(huì)先根據(jù)url調(diào)用以下方法獲取到唯一標(biāo)示再進(jìn)行后續(xù)操作岳枷。

- (NSString *)cacheKeyForURL:(NSURL *)url {
    if (!url) {
        return @"";
    }
    
    if (self.cacheKeyFilter) {
        return self.cacheKeyFilter(url);
    } else {
        return [url absoluteString];
    }
}

官方給出了設(shè)置的方式

[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) {
    url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
    return [url absoluteString];
}];

YYImage是如何做到逐行掃描的芒填?

一般來說呜叫,圖片下載,是通過將圖片數(shù)據(jù)轉(zhuǎn)換為NSData類型殿衰,然后順序進(jìn)行傳遞朱庆,接收到后再拼接的。YYImage的逐行掃描是因?yàn)閳D片本身有一個(gè)屬性interlace闷祥,只有設(shè)置了這個(gè)屬性才可能實(shí)現(xiàn)娱颊。(參考http://blog.csdn.net/code_for_free/article/details/51290067


如果本文讓你有那么點(diǎn)覺得“I get”的感覺,請(qǐng)點(diǎn)個(gè)贊唄凯砍!寫作很辛苦箱硕,路過請(qǐng)點(diǎn)贊!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悟衩,一起剝皮案震驚了整個(gè)濱河市剧罩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌局待,老刑警劉巖斑响,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異钳榨,居然都是意外死亡舰罚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門薛耻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來营罢,“玉大人,你說我怎么就攤上這事饼齿∷茄” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵缕溉,是天一觀的道長(zhǎng)考传。 經(jīng)常有香客問我,道長(zhǎng)证鸥,這世上最難降的妖魔是什么僚楞? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮枉层,結(jié)果婚禮上泉褐,老公的妹妹穿的比我還像新娘。我一直安慰自己鸟蜡,他們只是感情好膜赃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著揉忘,像睡著了一般跳座。 火紅的嫁衣襯著肌膚如雪端铛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天躺坟,我揣著相機(jī)與錄音沦补,去河邊找鬼乳蓄。 笑死咪橙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的虚倒。 我是一名探鬼主播美侦,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼魂奥!你這毒婦竟也來了菠剩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤耻煤,失蹤者是張志新(化名)和其女友劉穎具壮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哈蝇,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棺妓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了炮赦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怜跑。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吠勘,靈堂內(nèi)的尸體忽然破棺而出性芬,到底是詐尸還是另有隱情,我是刑警寧澤剧防,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布植锉,位于F島的核電站,受9級(jí)特大地震影響峭拘,放射性物質(zhì)發(fā)生泄漏俊庇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一棚唆、第九天 我趴在偏房一處隱蔽的房頂上張望暇赤。 院中可真熱鬧,春花似錦宵凌、人聲如沸鞋囊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽溜腐。三九已至译株,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挺益,已是汗流浹背歉糜。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留望众,地道東北人匪补。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像烂翰,于是被迫代替她去往敵國和親夯缺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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