學(xué)習(xí)SDWebImage

1.SDWebImage有什么用

SDWebImage為UIImageView、UIImage、UIButton添加webcache分類

異步下載圖片

自動(dòng)的異步緩存,包括內(nèi)存緩存和磁盤緩存

在后臺(tái)解壓圖片

保證相同的url不會(huì)被重復(fù)訪問多次,保證錯(cuò)誤的url不會(huì)被反復(fù)請(qǐng)求

保證不會(huì)阻塞主線程

用block和arc實(shí)現(xiàn)

2.框架結(jié)構(gòu)

管理類:SDWebImageManager

處理緩存類:SDImageCache(基于NSCache類,線程安全的

處理圖片下載:SDWebImageDownloader->SDWebImageDownloaderOperation(真正處理下載操作的類)

3.關(guān)于內(nèi)存不足時(shí),清除緩存

當(dāng)內(nèi)存不足,發(fā)生內(nèi)存警告到時(shí)候需要清除緩存。不同的警告有不同的清除緩存的方式。

當(dāng)監(jiān)聽到UiApplicationDidReceiveMemoryWarningNotification的時(shí)候,也就是系統(tǒng)級(jí)的內(nèi)存警告,會(huì)調(diào)用clearMemory方法。

//清除內(nèi)存緩存

- (void)clearMemory {

? ? //把所有的內(nèi)存緩存都刪除

? ? [self.memCache removeAllObjects];

}

當(dāng)監(jiān)聽到UIApplicationWillTerminateNotification警告的時(shí)候,程序即將終止的時(shí)候(按home鍵),調(diào)用cleanDisk方法。

當(dāng)監(jiān)聽到UIApplicationDidEnterBackgroundNotification警告的時(shí)候,調(diào)用backgroundCleanDisk方法。

cleanDisk清除過期緩存,清除了過期緩存之后計(jì)算當(dāng)前緩存,和設(shè)置的最大緩存數(shù)做比較,如果當(dāng)前緩存大于最大緩存,要繼續(xù)清除,清除的時(shí)候,按照文件創(chuàng)建的時(shí)間順序,從最舊的開始清除。最大緩存數(shù)量:maxCacheSize,緩存圖像總大小,以字節(jié)為單位,默認(rèn)數(shù)值為0,表示不作限制。

過期時(shí)間:7天maxCacheAge

clearDisk直接把緩存全部刪除,然后重新創(chuàng)建一個(gè)文件夾。

清空過期的磁盤緩存:

//清除過期的磁盤緩存- (void)cleanDisk { [self cleanDiskWithCompletionBlock:nil];}

刪除過期磁盤緩存的具體步驟:

1.計(jì)算過期日期,比這個(gè)日期還早的文件就是過期文件

NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];

2.遍歷緩存路徑中的所有文件,刪除早于過期日期的所有文件,并保存文件屬性來計(jì)算緩存占用空間大小。

NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey]; if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) { [urlsToDelete addObject:fileURL]; continue; }

??:[modificationDate laterDate:expirationDate]返回的是modificationDate和:expirationDate中更晚的一個(gè)日期,如果返回expirationDate就代表這個(gè)文件最早的修改時(shí)間比過期日期還早,這個(gè)時(shí)候就要?jiǎng)h除它。

3.// 刪除過期的文件 for (NSURL *fileURL in urlsToDelete) { [_fileManager removeItemAtURL:fileURL error:nil]; }

4.計(jì)算磁盤緩存,如果剩余磁盤緩存超過最大限額繼續(xù)刪除,從最舊的文件開始刪除。

??:面試的時(shí)候被問到了一個(gè)問題,如何去判斷一個(gè)圖片有沒有超過最大緩存時(shí)間呢?

圖片的修改時(shí)間就是圖片的一個(gè)屬性,用這個(gè)時(shí)間和最大緩存時(shí)間去比較,如果比最大緩存時(shí)間還早就說明是過期圖片。

FOUNDATION_EXPORT NSString * const NSURLContentModificationDateKey NS_AVAILABLE(10_6, 4_0);

4.下載圖片核心代碼

給UIImageView設(shè)置圖片需要用到UIImageView+WebCache這個(gè)類提供的接口,這個(gè)類提供了多個(gè)加載圖片的借口,但是核心都是調(diào)用一個(gè)方法。

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {}

這個(gè)方法的實(shí)現(xiàn)流程:

1. 取消當(dāng)前圖像下載?

[self sd_cancelCurrentImageLoad];

下載圖片之前先取消下載這個(gè)UIImageView之前要下載的圖片。因?yàn)橐呀?jīng)要下載新的一張圖片了,原來要下載什么已經(jīng)不重要了。這個(gè)方法的本質(zhì)是把operationDictionary字典中對(duì)應(yīng)的操作移除了。這個(gè)operationDictionary字典里存儲(chǔ)了所有的下載操作。

??:這個(gè)方法可以用來解決一個(gè)問題,因?yàn)閁ITableView里的cell是重用的,一個(gè)cell上的imageView開啟了圖片下載的方法,這個(gè)時(shí)候這個(gè)cell被重用了,新的cell上的imageView又開啟了一個(gè)下載方法,兩個(gè)下載操作回調(diào)給同一個(gè)imageView,就會(huì)造成數(shù)據(jù)的錯(cuò)亂。所以,在開始下載之前要把之前的下載操作取消。

2.設(shè)置占位圖片placeholder

//判斷,如果傳入的下載策略不是延遲顯示占位圖片,那么在主線程中設(shè)置占位圖片

?if (!(options & SDWebImageDelayPlaceholder)) {

? ? ? ? dispatch_main_async_safe(^{

? ? ? ? ? ? // 設(shè)置占位圖像

? ? ? ? ? ? self.image = placeholder;

? ? ? ? });

? ? }

3.判斷url是否為空,如果為空則生成一個(gè)錯(cuò)誤信息,把錯(cuò)誤信息回傳

4.如果url不為空,創(chuàng)建一個(gè)新的下載操作

id operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {}

創(chuàng)建下載操作時(shí),會(huì)調(diào)用SDWebImageManager中的方法

- (id)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options ?progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {};

這個(gè)方法的邏輯:

4.1.檢查圖片的URL,先判斷URL的數(shù)據(jù)類型是否正確,如果不正確需要賦值為nil,防止因參數(shù)類型錯(cuò)誤導(dǎo)致程序崩潰。

if (![url isKindOfClass:NSURL.class]) { url = nil; }

4.2.檢查URL是否在URL黑名單里,黑名單里存儲(chǔ)曾經(jīng)下載失敗的URL。這也就避免了請(qǐng)求失敗的URL不會(huì)被多次請(qǐng)求。

@synchronized (self.failedURLs) {

? ? ? ? isFailedUrl = [self.failedURLs containsObject:url];

? ? }failedURLs是一個(gè)NSMutableSet,用來存放請(qǐng)求失敗的URL。

4.3.如果URL不正確,或者URL存放在URL黑名單里但是沒有選擇請(qǐng)求失敗重新下載的策略,就直接返回。回調(diào)completedBlock塊,把錯(cuò)誤信息返回。

4.4.URL正確,添加當(dāng)前任務(wù)到正在下載的任務(wù)數(shù)組中。

@synchronized (self.runningOperations) {

? ? ? ? [self.runningOperations addObject:operation];

? ? }

4.5.根據(jù)URL生成一個(gè)key,用來對(duì)應(yīng)圖片的緩存。

NSString *key = [self cacheKeyForURL:url];

4.6.根據(jù)key值檢查圖片緩存是否存在,在SDWebImageManager里調(diào)用

operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {}方法。

相當(dāng)于調(diào)用SDImageCache里的

- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {}這個(gè)方法。

先檢查緩存對(duì)應(yīng)的key是否為空,如果為空直接返回doneBlock。

先檢查圖片在內(nèi)存緩存中是否存在

UIImage *image = [self imageFromMemoryCacheForKey:key];

? ? if (image) {

? ? ? ? doneBlock(image, SDImageCacheTypeMemory);

? ? ? ? return nil;}

如果圖片存在,就直接把圖片返回,并且把圖片的緩存方式(內(nèi)存緩存)返回。檢查的時(shí)候其實(shí)就是用kvc在memCache這個(gè)NSCache里檢查有沒有對(duì)應(yīng)的value。稍后我們具體看一下NSCache是怎么用的。

如果內(nèi)存緩存不存在,就去檢查磁盤緩存。

開啟子線程,檢查圖片是否在磁盤緩存里。

UIImage *diskImage = [self diskImageForKey:key];

NSData *data = [self diskImageDataBySearchingAllPathsForKey:key]; ?如果data為空,就代表緩存沒有命中。

根據(jù)key在默認(rèn)路徑和自定義路徑下面找:

NSString *defaultPath = [self defaultCachePathForKey:key];

NSArray *customPaths = [self.customPaths copy];

NSString *filePath = [self cachePathForKey:key inPath:path];//這個(gè)path是存在customPath這個(gè)數(shù)組里的,用戶自定義的路徑。

如果磁盤緩存存在,計(jì)算圖片的大小,然后保存到內(nèi)存緩存里。

if (diskImage && self.shouldCacheImagesInMemory) {

? ? ? ? ? ? ? ? NSUInteger cost = SDCacheCostForImage(diskImage);

? ? ? ? ? ? ? ? [self.memCache setObject:diskImage forKey:key cost:cost]; }

然后把圖片和圖片的緩存方式(磁盤緩存)返回,在主線程返回。

如果內(nèi)存緩存和磁盤緩存都沒有命中,就開始下載圖片。

4.7.下載圖片調(diào)用SDWebImageDownloader的方法。設(shè)置一個(gè)下載超時(shí)時(shí)間,默認(rèn)是15秒。真正的下載操作在SDWebImageDownloaderOperation里。

創(chuàng)建一個(gè)SDWebImageDownloaderOperation對(duì)象,當(dāng)這個(gè)SDWebImageDownloaderOperation對(duì)象被添加到隊(duì)列downloadQueue的時(shí)候,就會(huì)自動(dòng)調(diào)用start方法。在start方法中,創(chuàng)建NSURLConnection請(qǐng)求。

//創(chuàng)建NSURLConnection對(duì)象,并設(shè)置代理(沒有馬上發(fā)送請(qǐng)求)

? ? ? ? self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];

//獲得當(dāng)前線程 self.thread = [NSThread currentThread];

發(fā)出開始下載圖片的請(qǐng)求,然后開啟runloop直到請(qǐng)求結(jié)束。

[self.connection start]; //發(fā)送網(wǎng)絡(luò)請(qǐng)求

//開啟Runloop ? CFRunLoopRun();

下載圖片的過程中通過NSURLConnection的代理方法接受數(shù)據(jù),第一個(gè)方法 - connection:didReceiveResponse: 被調(diào)用后,接著會(huì)多次調(diào)用 - connection:didReceiveData: 方法來更新進(jìn)度、拼接圖片數(shù)據(jù),當(dāng)圖片數(shù)據(jù)全部下載完成時(shí),- connectionDidFinishLoading: 方法就會(huì)被調(diào)用。

??:下面具體分析一下這三個(gè)代理方法的實(shí)現(xiàn):

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

1.首先要判斷請(qǐng)求回傳的狀態(tài)碼,如果狀態(tài)碼是請(qǐng)求成功,就繼續(xù)執(zhí)行,如果是304或者是其他請(qǐng)求失敗狀態(tài)碼就取消請(qǐng)求操作。

2.//初始化可變的Data用來接收?qǐng)D片數(shù)據(jù) self.imageData = [[NSMutableData alloc] initWithCapacity:expected];

3. //得到請(qǐng)求的響應(yīng)頭信息 self.response = response;

4.//注冊(cè)通知中心,在主線程中發(fā)送通知SDWebImageDownloadReceiveResponseNotification【接收到服務(wù)器的響應(yīng)】 dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:self]; });}

接受到服務(wù)器返回的數(shù)據(jù)以后調(diào)用該方法,并且調(diào)用多次。

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

//不斷拼接接收到的圖片數(shù)據(jù)(二進(jìn)制數(shù)據(jù))

[self.imageData appendData:data];

處理圖片下載的進(jìn)度

}

圖片下載完成以后,調(diào)用該方法:

- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {

@synchronized(self) {

? ? ? ? //關(guān)停當(dāng)前的runloop

? ? ? ? CFRunLoopStop(CFRunLoopGetCurrent());

? ? ? ? //把線程和連接對(duì)象清空

? ? ? ? self.thread = nil;

? ? ? ? self.connection = nil;

? ? ? ? //在主線程中發(fā)出通知:

? ? ? ? //SDWebImageDownloadStopNotification? ? 任務(wù)停止

? ? ? ? //SDWebImageDownloadFinishNotification? 任務(wù)完成

? ? ? ? dispatch_async(dispatch_get_main_queue(), ^{

? ? ? ? ? ? [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];

? ? ? ? ? ? [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:self];

??:如果請(qǐng)求過程中發(fā)生了錯(cuò)誤,就只發(fā)送請(qǐng)求停止的通知,而不要發(fā)送請(qǐng)求結(jié)束的通知。

? ? ? ? });

把得到的圖片的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成圖片

UIImage *image = [UIImage sd_imageWithData:self.imageData];

根據(jù)url獲得緩存key,對(duì)圖片進(jìn)行縮放和解壓

if (self.shouldDecompressImages) { image = [UIImage decodedImageWithImage:image]; }

? ? }

}

4.8.下載完成后回到SDWebImageManager,根據(jù)得到的圖片進(jìn)行緩存處理(SDImageCache)。

4.9.把operation返回,給控件設(shè)置圖片。

5.將創(chuàng)建好的操作添加到操作字典里?,實(shí)現(xiàn)多張圖片同時(shí)下載

[self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];

5.如何給圖片命名

將圖片的url進(jìn)行md5加密,得到一個(gè)字符串就是圖片的名稱。

6.如何判斷圖片類型

只判斷圖片的第一個(gè)字節(jié),因?yàn)椴煌愋偷氖M(jìn)制數(shù)據(jù)的第一個(gè)字節(jié)是不一樣的,相同類型圖片的第一個(gè)字節(jié)是一樣的。

7.??

使用SDWebImageManager來下載圖片,自動(dòng)就會(huì)進(jìn)行圖片的內(nèi)存緩存和磁盤緩存。

單獨(dú)使用SDWebImageDownloader下載圖片,不會(huì)進(jìn)行緩存。

單獨(dú)使用SDImageCache異步緩存圖片,緩存圖片時(shí)可以選擇緩存策略,是同時(shí)進(jìn)行內(nèi)存緩存和磁盤緩存,還是單獨(dú)選擇一種緩存方式。

讀取緩存的時(shí)候,根據(jù)圖片url生成的唯一的key,就可以在緩存里讀取到對(duì)應(yīng)的圖片。

8.圖片解碼

使用SDWebImageDecoder進(jìn)行圖片解碼

首先我們要知道為什么要對(duì)圖片進(jìn)行解碼,從網(wǎng)絡(luò)上下載下來的數(shù)據(jù)被緩存在磁盤里是png或者jpeg的格式,這種格式的圖片是不能直接顯示在imageView上面的,需要進(jìn)行解壓,將圖片解壓成位圖,這個(gè)過程是非常消耗CPU的。

??:兩種加載圖片的方式

[UIImageView setImage:XXX];

UIImageView的setImage的時(shí)候,這時(shí)候內(nèi)存才會(huì)增加.并且這時(shí)候你將imageView移除,內(nèi)存也不會(huì)有減少。

[UIImage ImageNamed: @"xxx.jpg"]

不適合加載大的不常用的圖片.因?yàn)樗鼤?huì)默認(rèn)在程序里保存這張圖片數(shù)據(jù)(不會(huì)隨ImageView的移除而移除).只有經(jīng)常使用圖片適合這種方式加載.

因?yàn)榻獯a的過程是在調(diào)用setImage方法的時(shí)候才進(jìn)行的,這個(gè)過程默認(rèn)是在主線程執(zhí)行的,為了防止主線程的卡頓,應(yīng)該將其優(yōu)化到在子線程執(zhí)行。

在子線程執(zhí)行圖片的解壓過程,然后對(duì)解壓后的圖片進(jìn)行緩存,在主線程顯示圖片的時(shí)候直接就可以用了。

9.URL如何管理?

就是我們請(qǐng)求加載的圖片地址,是以什么方式保存管理的呢?答案是Runtime中的objc_setAssociatedObjectobjc_getAssociatedObject方法,在運(yùn)行時(shí)動(dòng)態(tài)的將url值綁定到具體的對(duì)象(例如ImageView)中,以imageURLKey全局變量作為綁定值的key。

10.SDWebImage如何做到相同的url不去請(qǐng)求多次?

我們可以認(rèn)為url和圖片是一一對(duì)應(yīng)的,也就是說我們請(qǐng)求了這個(gè)url以后對(duì)應(yīng)的圖片就被保存到了緩存里,這個(gè)時(shí)候也就不需要重新發(fā)送url去請(qǐng)求了。而我們?yōu)榱烁鶕?jù)url找到緩存里的圖片,圖片的名稱就是對(duì)url進(jìn)行md5加密以后的字符串。

??:還有一個(gè)延伸的問題,就是如何保證在同一時(shí)間請(qǐng)求相同的url,只請(qǐng)求一次。因?yàn)橥粫r(shí)間發(fā)請(qǐng)求,那么請(qǐng)求到的結(jié)果還沒有緩存起來,就沒辦法根據(jù)緩存避免重復(fù)的請(qǐng)求。

解決方案:讀SDWebImage庫系列(1)-如何保證同一時(shí)間請(qǐng)求相同URL時(shí),只進(jìn)行一次網(wǎng)絡(luò)請(qǐng)求

有一個(gè)字典self.URLCallbacks,這個(gè)字典保存著url為key,self.URLCallbacks中的value是一個(gè)數(shù)組callbacksForURL,這是一個(gè)以字典為元素的數(shù)組,保存一些不同類型的回調(diào)如completeblock,progress block等,我們根據(jù)url找到callbacksForURL數(shù)組,就能找到個(gè)鐘類型的回調(diào),可以知道這個(gè)url請(qǐng)求的執(zhí)行狀態(tài)。

11.如果url相同但是url里圖片的內(nèi)容改變了怎么辦?

為什么會(huì)發(fā)生這種情況呢,就是我們默認(rèn)的url不改變的話那么圖片也不變,我們用這個(gè)url去請(qǐng)求的時(shí)候系統(tǒng)認(rèn)為他已經(jīng)在緩存里了,就不會(huì)重新去下載,這個(gè)時(shí)候要怎么解決呢?

將加載圖片的策略設(shè)置成SDWebImageRefreshCached。

NSURL *imgURL = [NSURL URLWithString:@"http://handy-img-storage.b0.upaiyun.com/3.jpg"];

[[self imageView] sd_setImageWithURL: imgURL ? placeholderImage:nil ?options:SDWebImageRefreshCached];?

然后修改SDWebImageDownloader的headersFilter,讓開發(fā)者對(duì)所有的圖片請(qǐng)求設(shè)置一些額外的header。

給請(qǐng)求添加一個(gè)If-Modified-Since屬性。

思路就是:

與服務(wù)器返回的Last-Modified相對(duì)應(yīng)的request header里可以加一個(gè)名為If-Modified-Since的key,value即是服務(wù)器回傳的服務(wù)端圖片最后被修改的時(shí)間,第一次圖片請(qǐng)求時(shí)If-Modified-Since的值為空,第二次及以后的客戶端請(qǐng)求會(huì)把服務(wù)器回傳的Last-Modified值作為If-Modified-Since的值傳給服務(wù)器,這樣服務(wù)器每次接收到圖片請(qǐng)求時(shí)就將If-Modified-Since與Last-Modified進(jìn)行比較。如果不同就代表圖片被更新了,返回200,如果返回304就代表這兩個(gè)值相同。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,615評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,826評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,227評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,447評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,992評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,807評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,001評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評(píng)論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,243評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評(píng)論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,709評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,996評(píng)論 2 374

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