SDWebImage源碼學(xué)習(xí)

前言

因為對大神的開源代碼非常崇拜修陡,所以開始學(xué)習(xí)這些開源的代碼媒抠。
這是一年前學(xué)習(xí)源碼時寫在印象筆記里的筆記,過了一年官硝,今天又把最新的SDWebImage下下來看,發(fā)現(xiàn)沒什么大變化短蜕。

分析

1.我們平時開發(fā),用的最多的就是:
- (void)sd_setImageWithURL:(nullable NSURL *)url 
          placeholderImage:(nullable UIImage *)placeholder

這一系列方法.使用非常簡單:

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://example.com/image.jpg"]
                      placeholderImage:[UIImage imageNamed:@"placeholder"]];
2.那這個方法是怎么實現(xiàn)的呢?
a.這個方法在UIImageView+WebCache分類中, 有一系列.當(dāng)最終這些方法都會調(diào)用下面這個.區(qū)別只是傳的參數(shù),要么傳了空,要么是默認(rèn)值.
/**
 <# 平時用的方法,只是對這個方法包裝了一下 #>

 @param url            <# 網(wǎng)絡(luò)圖片鏈接 #>
 @param placeholder    <# 占位圖 #>
 @param options        <# 一個特殊的標(biāo)記 #>
 @param progressBlock  <# 進(jìn)度回調(diào)block #>
 @param completedBlock <# 網(wǎng)絡(luò)圖像加載完成回調(diào)的block #>
 */
     - (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock;
           

b.上面這個方法又調(diào)用了UIView+WebCache分類的方法:

/**
 <# 根據(jù)指定的url,異步下載圖像,并緩存.設(shè)置imageView中的image#> 
 @param url            <#url 指定的url#>
 @param placeholder    <#placeholder 展占圖#>
 @param options        <#options 一個特殊的標(biāo)記#>
 @param operationKey   <#operationKey 下載操作(operation)的key值,默認(rèn)為類名#>
 @param setImageBlock  <#setImageBlock 自定義設(shè)置圖像的代碼 #>
 @param progressBlock  <#progressBlock 下載過程中進(jìn)度回調(diào)的block #>
 @param completedBlock <#completedBlock 下載完成回調(diào)的block #>
 */
           - (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock;
3.現(xiàn)在對這個方法的代碼一行行分析:
3.1
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
[self sd_cancelImageLoadOperationWithKey:validOperationKey];

a氢架、數(shù)據(jù)校驗,如果設(shè)置了operation的key就使用設(shè)置的,默認(rèn)使用類名作為key
b、根據(jù)operationKey找到對應(yīng)的操作,從下載隊列取消正在下載的操作
c忿危、使當(dāng)前 UIImageView 中的所有操作都被 cancel. 不會影響之后進(jìn)行的下載操作.
d达箍、這里只是說了,不取消,會影響后面的下載操作.那為什么會影響?影響的結(jié)果是什么?

為什么會影響:

因為UIImageView一個對象應(yīng)該只對應(yīng)的是一個下載操作,如果UIImageView的當(dāng)前對象,他的前一個下載操作未完成,然后又來了一個下載操作(同一個imageView多次調(diào)用了這個方法),這樣就有了2個下載操作了.這不符合一個UIImageView對象對應(yīng)一個下載操作.

影響的結(jié)果:

當(dāng)前一個下載操作將圖像請求完成,展示到imageView上,過一會后面的操作也請求完成,這樣又會再一次設(shè)置imageview展示的圖像.雖然這可能不會有太大的影響,但是至少是消耗性能了,做了沒必要的操作.

3.2 利用runtime,對url做一次retain
     objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
3.3
if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
        });
    }

a.如果傳入的 options 中沒有 SDWebImageDelayPlaceholder(默認(rèn)情況下 options == 0),
b.那么就會為 UIImageView 添加一個臨時的 image, 也就是占位圖.
c.SDWebImageDelayPlaceholder(我的理解就是,不讓用占位圖),取反就是允許使用占位圖

3.4
//檢查url不為空才去加載
     if (url) {
     }
//否則直接調(diào)用加載完成回調(diào)的block,并吧錯誤傳入block
     else{
     } 
//檢查activityView(指示器)是否可用
// check if activityView is enabled or not
     if ([self sd_showActivityIndicatorView])               
          [selfsd_addActivityIndicator];
     }
3.5調(diào)用 SDWebImageManager的對象方法去獲取圖像
/**
 <# 指定的url如果沒有緩沖,就去下載圖像 #>

 @param url            <#url 指定的url#>
 @param options        <#options 一個特殊的標(biāo)記#>
 @param progressBlock  <#progressBlock 下載過程中進(jìn)度回調(diào)的block#>
 @param completedBlock <#completedBlock 下載完成回調(diào)的block#>

 @return <#return value 下載操作#>
 */
     - (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock;

//在這個方法中,首先會驗證url的有效性
     if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    } 
    // Prevents app crashing on argument type error like sending NSNull instead of NSURL
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }
3.6創(chuàng)建了一個包裝了一個看著像下載操作的卻并不是下載操作的類SDWebImageCombinedOperation,

這個類本身不是操作類,只是他內(nèi)部有一個NSOperation屬性(注意:在這里,operation屬性還沒有被賦值,是nil).當(dāng)然這個類還遵循了一個協(xié)議,SDWebImageOperation.這個協(xié)議只有一個取消的方法-cancel.

__block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    __weak SDWebImageCombinedOperation *weakOperation = operation;
3.7去黑名單匹配當(dāng)前url是否是下載失敗過的url
BOOL isFailedUrl = NO;
    if (url) {
        @synchronized (self.failedURLs) {
            isFailedUrl = [self.failedURLs containsObject:url];
        }
    }
3.8如果url驗證不通過,直接調(diào)用加載完成的回調(diào)block,并且傳error出去
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
        return operation;
    }
3.9準(zhǔn)備下載,首先將下載操作添加到數(shù)組保存起來
 @synchronized (self.runningOperations) {
        [self.runningOperations addObject:operation];
    }
3.10根據(jù)url獲取到緩存的key
 NSString *key = [self cacheKeyForURL:url];

//在正真去網(wǎng)上下載數(shù)據(jù)之前,調(diào)用SDImageCache的對象方法,
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
3.11在這個方法中,首先會驗證緩存的key的有效性.無效,說明緩存中沒有找到對應(yīng)的圖像;調(diào)用block去下載;
if (!key) {
        if (doneBlock) {              //標(biāo)記好無緩存
            doneBlock(nil, nil, SDImageCacheTypeNone);
        }
        return nil;
    }
3.12查詢內(nèi)存緩存中是否能找到對應(yīng)的image,如果找到了,將image放doneBlock中傳出去,如果沒找到,繼續(xù)往后走
UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        NSData *diskData = nil;
        if ([image isGIF]) {
            diskData = [self diskImageDataBySearchingAllPathsForKey:key];
        }
        if (doneBlock) {
            doneBlock(image, diskData, SDImageCacheTypeMemory);
        }
        return nil;
    }
3.13然后再在磁盤緩存中查找,是否有對應(yīng)的image,在這,不管找沒找到,都將獲取diskImage(沒找到時為nil)放doneBlock中傳出去.最后返回operation.
   NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage && self.config.shouldCacheImagesInMemory) {
                NSUInteger cost = SDCacheCostForImage(diskImage);
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }

            if (doneBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
                });
            }
3.14現(xiàn)在開始分析doneBlock中做寫什么.當(dāng)從內(nèi)存緩存,磁盤緩存查找回來,就開始執(zhí)行這個block
typedef void(^SDCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType);

在這個block中,首先判斷剛才創(chuàng)建的operation是否已經(jīng)取消

 if (operation.isCancelled) {
         //將已經(jīng)取消的操作,從正在執(zhí)行的操作數(shù)組中移除 
        [self safelyRemoveOperationFromRunning:operation];
        return;
    }
//如果(
          (`圖片不存在` || `options包含SDWebImageRefreshCached(刷新緩存)`)
          && 不能響應(yīng)imageManager:shouldDownloadImageForURL:方法)
      ||(或者)  
                   (imageManager:shouldDownloadImageForURL:返回值為YES( 沒有緩存過的圖片將不會下載))
      if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]))
      
      //如果在緩存中找到了image,但是需要刷新緩存,嘗試重新下載
      if (cachedImage && options & SDWebImageRefreshCached) {
                // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
                // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
                [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            }
3.16 如果圖片不存在,或者設(shè)置了SDWebImageRefreshCached 需要刷新緩存,就在下載
 // download if no image or requested to refresh anyway, and download allowed by delegate
SDWebImageDownloaderOptions downloaderOptions = 0;
            if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
            if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
            if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
            if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
            if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
            if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
            if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
            if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;
           
            if (cachedImage && options & SDWebImageRefreshCached) {
                // force progressive off if image already cached but forced refreshing
                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
                // ignore image read from NSURLCache if image if cached but force refreshing
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
3.17調(diào)用 SDWebImageDownloader(下載器)的對象方法開始從網(wǎng)絡(luò)下載圖片:
 /**
 <# 根據(jù)url創(chuàng)建一個異步下載器實例,當(dāng)下載完成或下載失敗報錯時會通知代理 #>
 @param url            <#url 指定的url#>
 @param options        <#options 標(biāo)記 #>
 @param progressBlock  <# 進(jìn)度回調(diào)block #>
 @param completedBlock <# 網(wǎng)絡(luò)圖像加載完成回調(diào)的block #>
 @return <#return value description#>
 */
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock
3.18然后在上面的方法中,會立即調(diào)用另外一個方法并返回
/**
 <#Description#>
 @param progressBlock  <#progressBlock 進(jìn)度回調(diào)的block #>
 @param completedBlock <#completedBlock 加載完畢回調(diào)的block #>
 @param url            <#url 指定圖像的url #>
 @param createCallback <#createCallback 創(chuàng)建請求的block #>
 @return <#return value description#>
 */
     - (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
                                           completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
                                                   forURL:(nullable NSURL *)url
                                           createCallback:(SDWebImageDownloaderOperation *(^)())createCallback
3.19在這個方法中,首先驗證url不能為nil,因為后面保存callblock(回調(diào)的block)需要url作為key
// The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
 if (url == nil) {
        if (completedBlock != nil) {
            completedBlock(nil, nil, nil, NO);
        }
        return nil;
    } 
3.20
//dispatch_barrier_sync:在前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,而且它后面的任務(wù)等它執(zhí)行完成之后才會執(zhí)行
dispatch_barrier_sync(self.barrierQueue, ^{
        //根據(jù)url作為key去字典中獲取對應(yīng)的operation
        SDWebImageDownloaderOperation *operation = self.URLOperations[url];
        if (!operation) {
            //如果獲取的operation不存在,說明是第一次加載,需要回調(diào)createCallback,以保證請求被創(chuàng)建好
            operation = createCallback();
            //保存好operation
            self.URLOperations[url] = operation;

            __weak SDWebImageDownloaderOperation *woperation = operation;
            operation.completionBlock = ^{
              //這個block,是在請求執(zhí)行完成時回調(diào)
              //當(dāng)請求執(zhí)行完畢,需要將URLOperations中保存的operation移除
              SDWebImageDownloaderOperation *soperation = woperation;
              if (!soperation) return;
              if (self.URLOperations[url] == soperation) {
                  [self.URLOperations removeObjectForKey:url];
              };
            };
        }

    //在這調(diào)用operation的對象方法,把progressBlock和completedBlock以字典的形式保存起來,并返回.
    //讓downloadOperationCancelToken接收這個字典,所以downloadOperationCancelToken其實是一個可變字典
 id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];

                 //與每個下載相關(guān)的令牌铺厨《忻担可以用來取消下載
        token = [SDWebImageDownloadToken new];
        token.url = url;
        token.downloadOperationCancelToken = downloadOperationCancelToken;
    });
 然后返回這個token(令牌).

 //看到這里,似乎我們根本就沒看見請求在哪.其實,前面有提到,當(dāng)根據(jù)url去獲取operation為空時,會回調(diào)一個createCallback
 //對,就是在createCallback中創(chuàng)建了請求,并開始下載的.
 //下面看看這個block中的代碼

 //首先會設(shè)置超時時間,默認(rèn)15s
  NSTimeInterval timeoutInterval = sself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }
 // 為防止重復(fù)緩存(NSURLCache + SDImageCache),如果設(shè)置了 SDWebImageDownloaderUseNSURLCache解滓,則禁用 SDImageCache
   這個 request 就用于在之后發(fā)送 HTTP 請求.
// 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 (sself.headersFilter) {
            request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
        }
        else {
            request.allHTTPHeaderFields = sself.HTTPHeaders;
        }
 //在初始化了這個 request 之后, 又初始化了一個 SDWebImageDownloaderOperation 的實例,
    //這個實例, 就是用于請求網(wǎng)絡(luò)資源的操作. 它是一個 NSOperation 的子類,
 SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
        operation.shouldDecompressImages = sself.shouldDecompressImages;

      // 設(shè)置 https 訪問時身份驗證使用的憑據(jù)
       if (sself.urlCredential) {
            operation.credential = sself.urlCredential;
        } else if (sself.username && sself.password) {
            operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
        }
  //將操作添加到隊列
  //只有將它加入到這個下載隊列中, 這個操作才會執(zhí)行.也就是說operation 的 `start` 方法會被執(zhí)行.開始請求網(wǎng)路下載圖像
[sself.downloadQueue addOperation:operation];
 
     //現(xiàn)在開始分析operation 的 `start` 方法里面做了些什么
          //整個下載都是加鎖的,安全
           @synchronized (self) {...}

     //如果操作已經(jīng)取消,設(shè)置已完成標(biāo)記,重置操作
      if (self.isCancelled) {
            self.finished = YES;
            [self reset];
            return;
        }
 //下面這段代碼,最主要的就是,保證程序進(jìn)入后臺,也能保證app中的線程繼續(xù)工作
 //beginBackgroundTaskWithExpirationHandler只要調(diào)用了此函數(shù)系統(tǒng)就會允許app的所有線程繼續(xù)執(zhí)行赃磨,直到任務(wù)結(jié)束(1,     
[[UIApplicationsharedApplication]backgroundTimeRemaining] 的時間結(jié)束 2,調(diào)用endBackgroundTask)
      Class UIApplicationClass = NSClassFromString(@"UIApplication");
        BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];
        if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
            __weak __typeof__ (self) wself = self;
            UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
            self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
               //如果在系統(tǒng)規(guī)定的時間內(nèi)任務(wù)還沒有完成,在時間到之前會調(diào)用這個block,一般是10分鐘
            __strong__typeof(wself) sself = wself;

                if (sself) {
                    [sself cancel];

                    [app endBackgroundTask:sself.backgroundTaskId];
                    sself.backgroundTaskId = UIBackgroundTaskInvalid;
                }
            }];
        }
3.21創(chuàng)建session
NSURLSession *session = self.unownedSession;
        if (!self.unownedSession) {
            NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
            sessionConfig.timeoutIntervalForRequest = 15;
           
            /**
             *  Create the session for this task
             *  We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
             *  method calls and completion handler calls.
             */
            self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
                                                              delegate:self
                                                         delegateQueue:nil];
            session = self.ownedSession;
        }
       
        self.dataTask = [session dataTaskWithRequest:self.request];
        self.executing = YES;
3.22
//啟動任務(wù),到這里我們用到的UIImageVIew+WebCache里面的方法sd_ImageWithURL:這系列方法是如何達(dá)到效果的就分析完畢了
[self.dataTask resume];
3.23啟動任務(wù)之后,調(diào)用progressBlock,發(fā)送開始下載的通知
 for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
            progressBlock(0, NSURLResponseUnknownLength, self.request.URL);
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
        });
3.24這個方法的操作可以保證新的操作隊列不會被舊的影響,同時把該清理的狀態(tài)都?xì)w為完畢
//任務(wù)完成洼裤,處理釋放對象 
      if (self.backgroundTaskId != UIBackgroundTaskInvalid) {
        UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)];
        [app endBackgroundTask:self.backgroundTaskId];
        self.backgroundTaskId = UIBackgroundTaskInvalid;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邻辉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子腮鞍,更是在濱河造成了極大的恐慌值骇,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件移国,死亡現(xiàn)場離奇詭異吱瘩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)迹缀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門使碾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人祝懂,你說我怎么就攤上這事票摇。” “怎么了砚蓬?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵矢门,是天一觀的道長。 經(jīng)常有香客問我,道長祟剔,這世上最難降的妖魔是什么傅事? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮峡扩,結(jié)果婚禮上蹭越,老公的妹妹穿的比我還像新娘。我一直安慰自己教届,他們只是感情好响鹃,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著案训,像睡著了一般买置。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上强霎,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天忿项,我揣著相機(jī)與錄音,去河邊找鬼城舞。 笑死轩触,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的家夺。 我是一名探鬼主播脱柱,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拉馋!你這毒婦竟也來了榨为?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤煌茴,失蹤者是張志新(化名)和其女友劉穎随闺,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蔓腐,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡矩乐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了合住。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绰精。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡撒璧,死狀恐怖透葛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卿樱,我是刑警寧澤僚害,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響萨蚕,放射性物質(zhì)發(fā)生泄漏靶草。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一岳遥、第九天 我趴在偏房一處隱蔽的房頂上張望奕翔。 院中可真熱鬧,春花似錦浩蓉、人聲如沸派继。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驾窟。三九已至,卻和暖如春认轨,著一層夾襖步出監(jiān)牢的瞬間绅络,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工嘁字, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留恩急,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓纪蜒,卻偏偏與公主長得像假栓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子霍掺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

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