iOS內(nèi)功篇:淺談Crash

iOS開發(fā)中,解決Crash相信是開發(fā)者最為頭疼的問(wèn)題了,特別是對(duì)于已上線的應(yīng)用,對(duì)其Crash的跟蹤和修復(fù)顯得尤其重要,本文主要總結(jié)了常見的Crash類型以及主流的Crash日志收集及解析的解決方案。

Crash分為兩種,未捕獲的Objective-C異常和Mach異常。

一、Objective-C Exception

在OC層面(iOS庫(kù)、第三方庫(kù)出現(xiàn)錯(cuò)誤拋出)的異常稱為OC異常。
比如:

NSArray * array= @[@“s",@“x",@“m"];
[array objectAtIndex:4];

OC異常可以用try-catch抓住:

@try {
    NSArray * array= @[@“s",@“x",@“m"];
    [array objectAtIndex:4];
} @catch (NSException *exception) {
    NSLog(@"%@",exception);
}

使用此方法可以抓到當(dāng)前拋出的異常并阻止程序崩潰,然而蘋果爸爸并不推薦這樣去做。

-[__NSArrayI objectAtIndex:]: index 4 beyond bounds [0 .. 2]

常見的OC異常

以下內(nèi)容來(lái)自文章《iOS開發(fā)質(zhì)量的那些事》

從上圖可以看到,iOS開發(fā)中常見的異常包括以下幾種:
NSInvalidArgumentException
NSRangeException
NSGenericException
NSInternalInconsistencyException
NSFileHandleOperationException

NSInvalidArgumentException
非法參數(shù)異常(NSInvalidArgumentException)是 Objective - C 代碼最常出現(xiàn)的錯(cuò)誤,所以平時(shí)在寫代碼的時(shí)候,需要多加注意,加強(qiáng)對(duì)參數(shù)的檢查,避免傳入非法參數(shù)導(dǎo)致異常,其中尤以nil參數(shù)為甚。
1. 集合數(shù)據(jù)的參數(shù)傳遞
比如NSMutableArray, NSMutableDictionary的數(shù)據(jù)操作
 (1) NSDictionary不能刪除nil的key       
 (2) NSDictionary不能添加nil的對(duì)象
 (3) 不能插入nil的對(duì)象
 (4) 其他一些nil參數(shù)
               
2. 其他一些API的使用
APP一般都會(huì)有網(wǎng)絡(luò)操作,免不了使用網(wǎng)絡(luò)相關(guān)接口,比如NSURL的初始化,不能傳入nil的http地址:
          
3. 未實(shí)現(xiàn)的方法
(1) .h文件里函數(shù)名,卻忘了修改.m文件里對(duì)應(yīng)的函數(shù)名
(2) 使用第三方庫(kù)時(shí),沒(méi)有添加”-ObjC” flag
(3) MRC時(shí),大部分情況下是因?yàn)閷?duì)象被提前release了,在你心里不希望他release的情況下,指針還在,對(duì)象已經(jīng)不在 了。

NSRangeException
越界異常(NSRangeException)也是比較常出現(xiàn)的異常,有如下幾種類型:
1. 數(shù)組最大下標(biāo)處理錯(cuò)誤
比如數(shù)組長(zhǎng)度count, index的下標(biāo)范圍[0, count -1], 在開發(fā)時(shí),可能index的最大值超過(guò)數(shù)組的范圍;
        
2. 下標(biāo)的值是其他變量賦值
這樣會(huì)有很大的不確定性, 可能是一個(gè)很大的整數(shù)值

3. 使用空數(shù)組
如果一個(gè)數(shù)組剛剛初始化,還是空的,就對(duì)它進(jìn)行相關(guān)操作
      
所以,為了避免NSRangeException的發(fā)生,必須對(duì)傳入的index參數(shù)進(jìn)行合法性檢查,是否在集合數(shù)據(jù)的個(gè)數(shù)范圍內(nèi)。

NSGenericException
NSGenericException這個(gè)異常最容易出現(xiàn)在foreach操作中,在for in循環(huán)中如果修改所遍歷的數(shù)組,無(wú)論你是add或remove,都會(huì)出錯(cuò) "for in",它的內(nèi)部遍歷使用了類似 Iterator進(jìn)行迭代遍歷,一旦元素變動(dòng),之前的元素全部被失效,所以在foreach的循環(huán)當(dāng)中,最好不要去進(jìn)行元素的修改動(dòng)作,若需要修改,循環(huán)改為for遍歷,由于內(nèi)部機(jī)制不同,不會(huì)產(chǎn)生修改后結(jié)果失效的問(wèn)題。
            
NSInternalInconsistencyException
不一致導(dǎo)致出現(xiàn)的異常
比如NSDictionary當(dāng)做NSMutableDictionary來(lái)使用,從他們內(nèi)部的機(jī)理來(lái)說(shuō),就會(huì)產(chǎn)生一些錯(cuò)誤
NSMutableDictionary *info = method return to NSDictionary type;
[info  setObject:@“sxm" forKey:@"name"]; 
比如xib界面使用或者約束設(shè)置不當(dāng)
        
NSFileHandleOperationException
處理文件時(shí)的一些異常,最常見的還是存儲(chǔ)空間不足的問(wèn)題,比如應(yīng)用頻繁的保存文檔,緩存資料或者處理比較大的數(shù)據(jù):
       
所以在文件處理里,需要考慮到手機(jī)存儲(chǔ)空間的問(wèn)題。

NSMallocException
這也是內(nèi)存不足的問(wèn)題,無(wú)法分配足夠的內(nèi)存空間 

OC異常的抓取和分析

在debug環(huán)境下,OC異常導(dǎo)致崩潰時(shí)Xcode控制臺(tái)會(huì)輸出完整的異常信息,比如:
Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘this is reason description’,包括Exception的類型、原因和發(fā)生異常的完整堆棧。

這些信息一般來(lái)說(shuō)都足夠詳細(xì),足夠我們輕易地找到異常的位置并進(jìn)行修復(fù)。

非debug環(huán)境下,可以通過(guò)注冊(cè) NSUncaughtExceptionHandler 捕獲異常信息。雖然無(wú)法阻止APP崩潰,但是可以獲取異常信息并進(jìn)行收集,下次啟動(dòng)APP時(shí)進(jìn)行上報(bào),方便開發(fā)者進(jìn)行錯(cuò)誤跟蹤及修復(fù),這就是常用Crash收集工具所做的事情。

void InstallUncaughtExceptionHandler(void) {
    NSSetUncaughtExceptionHandler(&handleUncaughtException);
}

void handleUncaughtException(NSException *exception) {
    NSString * crashInfo = [NSString stringWithFormat:@"Exception name:%@\nException reason:%@\nException stack:%@",[exception name], [exception reason], [exception callStackSymbols]];
    [WZCrashReporter saveCrash:crashInfo];
}

Mach Exception

Mach異常是指最底層的內(nèi)核級(jí)異常。

最常見的Mach異常:EXC_BAD_ACCESS (Bad Memory Access)

這種內(nèi)存訪問(wèn)異常分為訪問(wèn)非法地址(SIGBUS信號(hào))和訪問(wèn)了被回收掉的內(nèi)存(SIGSEGV信號(hào)),實(shí)際開發(fā)中遇到的錯(cuò)誤通常令人莫名其妙,往往需要大量時(shí)間來(lái)排查,非常頭疼。

EXC_BAD_ACCESS后面通常帶有code來(lái)幫助我們判斷到底是什么錯(cuò)誤,比如EXC_I386_GPFLT指訪問(wèn)了一塊已經(jīng)不屬于你的內(nèi)存。

一些其他的Mach異常:
  1. EXC_BAD_INSTRUCTION運(yùn)行了非法的指令,往往是運(yùn)行指令的參數(shù)不對(duì)(0或者nil的參數(shù))
  2. EXC_RESOURCE程序資源上限(cpu占用過(guò)高或者內(nèi)存不足)。
  3. EXC_GUARD一些C函數(shù)訪問(wèn)錯(cuò)誤導(dǎo)致的異常。
  4. 0x00000020奇怪異常集合,常見的是由于主線程阻塞看門狗殺死了APP《Exception Type: 00000020:什么是看門狗機(jī)制》

Unix Signal Exception

從Mach異常最終會(huì)轉(zhuǎn)化成Unix信號(hào)投遞到出錯(cuò)的線程(具體原理可以學(xué)習(xí)《漫談iOS Crash收集框架》, 各種信號(hào)的含義可以學(xué)習(xí)《iOS異常捕獲》

  1. OC異常并不是真正的異常,但是當(dāng)一個(gè)OC異常被拋出到最外層還沒(méi)被捕獲,程序會(huì)強(qiáng)行發(fā)送SIGABRT信號(hào)中斷程序。
  2. Mach異常沒(méi)有比較便利的捕獲方式,既然它最終會(huì)轉(zhuǎn)化成信號(hào),我們也可以通過(guò)捕獲信號(hào),來(lái)捕獲 Crash 事件。

iOS提供了signal方法來(lái)注冊(cè)一個(gè)處理函數(shù),在處理函數(shù)中,使用execinfo中的 backtrace_symbols取出匯編層程序的堆棧信息。

代碼如下:

void InstallSignalHandler(void) {
    signal(SIGHUP, handleSignalException);
    signal(SIGINT, handleSignalException);
    signal(SIGQUIT, handleSignalException);
    
    signal(SIGABRT, handleSignalException);
    signal(SIGILL, handleSignalException);
    signal(SIGSEGV, handleSignalException);
    signal(SIGFPE, handleSignalException);
    signal(SIGBUS, handleSignalException);
    signal(SIGPIPE, handleSignalException);
}

void handleSignalException(int signal) {
    NSMutableString * crashInfo = [[NSMutableString alloc]init];
    [crashInfo appendString:[NSString stringWithFormat:@"signal:%d\n",signal]];
    [crashInfo appendString:@"Stack:\n"];
    void* callstack[128];
    int i, frames = backtrace(callstack, 128);
    char** strs = backtrace_symbols(callstack, frames);
    for (i = 0; i <frames; ++i) {
        [crashInfo appendFormat:@"%s\n", strs[i]];
    }
    [WZCrashReporter saveCrash:crashInfo];
}

下面是一些常用信號(hào)代表的含義:

(1)  SIGHUP
本信號(hào)在用戶終端連接(正常或非正常)結(jié)束時(shí)發(fā)出, 通常是在終端的控制進(jìn)程結(jié)束時(shí), 通知同一session內(nèi)的各個(gè)作業(yè), 這時(shí)它們與控制終端不再關(guān)聯(lián)。
登錄Linux時(shí),系統(tǒng)會(huì)分配給登錄用戶一個(gè)終端(Session)。在這個(gè)終端運(yùn)行的所有程序,包括前臺(tái)進(jìn)程組和后臺(tái)進(jìn)程組,一般都屬于這個(gè) Session。當(dāng)用戶退出Linux登錄時(shí),前臺(tái)進(jìn)程組和后臺(tái)有對(duì)終端輸出的進(jìn)程將會(huì)收到SIGHUP信號(hào)。這個(gè)信號(hào)的默認(rèn)操作為終止進(jìn)程,因此前臺(tái)進(jìn) 程組和后臺(tái)有終端輸出的進(jìn)程就會(huì)中止。不過(guò)可以捕獲這個(gè)信號(hào),比如wget能捕獲SIGHUP信號(hào),并忽略它,這樣就算退出了Linux登錄, wget也 能繼續(xù)下載。
此外,對(duì)于與終端脫離關(guān)系的守護(hù)進(jìn)程,這個(gè)信號(hào)用于通知它重新讀取配置文件。
(2)  SIGINT
程序終止(interrupt)信號(hào), 在用戶鍵入INTR字符(通常是Ctrl-C)時(shí)發(fā)出,用于通知前臺(tái)進(jìn)程組終止進(jìn)程。
(3)  SIGQUIT
和SIGINT類似, 但由QUIT字符(通常是Ctrl-)來(lái)控制. 進(jìn)程在因收到SIGQUIT退出時(shí)會(huì)產(chǎn)生core文件, 在這個(gè)意義上類似于一個(gè)程序錯(cuò)誤信號(hào)。
(6)  SIGABRT
調(diào)用abort函數(shù)生成的信號(hào)。
(7)  SIGBUS
非法地址, 包括內(nèi)存地址對(duì)齊(alignment)出錯(cuò)。比如訪問(wèn)一個(gè)四個(gè)字長(zhǎng)的整數(shù), 但其地址不是4的倍數(shù)。它與SIGSEGV的區(qū)別在于后者是由于對(duì)合法存儲(chǔ)地址的非法訪問(wèn)觸發(fā)的(如訪問(wèn)不屬于自己存儲(chǔ)空間或只讀存儲(chǔ)空間)。
(8)  SIGFPE
在發(fā)生致命的算術(shù)運(yùn)算錯(cuò)誤時(shí)發(fā)出. 不僅包括浮點(diǎn)運(yùn)算錯(cuò)誤, 還包括溢出及除數(shù)為0等其它所有的算術(shù)的錯(cuò)誤。
(9)  SIGKILL
用來(lái)立即結(jié)束程序的運(yùn)行. 本信號(hào)不能被阻塞、處理和忽略。如果管理員發(fā)現(xiàn)某個(gè)進(jìn)程終止不了,可嘗試發(fā)送這個(gè)信號(hào)。
(11)  SIGSEGV
試圖訪問(wèn)未分配給自己的內(nèi)存, 或試圖往沒(méi)有寫權(quán)限的內(nèi)存地址寫數(shù)據(jù).
(13)  SIGPIPE
管道破裂。這個(gè)信號(hào)通常在進(jìn)程間通信產(chǎn)生,比如采用FIFO(管道)通信的兩個(gè)進(jìn)程,讀管道沒(méi)打開或者意外終止就往管道寫,寫進(jìn)程會(huì)收到SIGPIPE信號(hào)。此外用Socket通信的兩個(gè)進(jìn)程,寫進(jìn)程在寫Socket的時(shí)候,讀進(jìn)程已經(jīng)終止。

Crash日志收集

Crash日志收集方式

1.蘋果Crash收集服務(wù)

新版iTunes Connect已不能看到APP的crash日志,只能在XCode 中Window->Organizer->Crashes可以看到crash日志。

當(dāng)程序運(yùn)行Crash的時(shí)候,系統(tǒng)會(huì)把運(yùn)行的最后時(shí)刻的運(yùn)行信息記錄下來(lái),存儲(chǔ)到一個(gè)文件中,也就是我們所說(shuō)的Crash文件,但收集crash功能需要用戶設(shè)置->隱私->診斷與用量->診斷與用量數(shù)據(jù)選擇自動(dòng)發(fā)送,并與開發(fā)者共享,由于不是所有用戶都會(huì)把這個(gè)功能打開,所以并不能保證收集到所有的Crash信息,推薦指數(shù)三顆星。

2.自行實(shí)現(xiàn)Crash收集及上報(bào)框架

實(shí)現(xiàn)原理上面已詳細(xì)描述,適合人手充足,技術(shù)儲(chǔ)備足夠的團(tuán)隊(duì)使用,推薦指數(shù)五顆星。

3.第三方crash收集服務(wù)

騰訊bugly、友盟等Crash收集服務(wù)比較完善,作為開發(fā)者省心省力,適合個(gè)人或者對(duì)隱私性要求不高的團(tuán)隊(duì)使用,推薦指數(shù)五顆星。

Crash日志收集的沖突

以下內(nèi)容來(lái)自網(wǎng)絡(luò)文章

在我們自己研發(fā) Crash 收集框架之前,最早肯定都會(huì)接入騰訊 Bugly、友盟等第三方日志框架來(lái)進(jìn)行崩潰的收集和分析。如果多個(gè) Crash 收集框架存在時(shí),往往會(huì)存在沖突。
不管是對(duì)于 Signal 捕獲還是 NSException 捕獲都會(huì)存在 handler 覆蓋的問(wèn)題,正確的做法應(yīng)該是先判斷是否有前者已經(jīng)注冊(cè)了 handler,如果有則應(yīng)該把這個(gè) handler 保存下來(lái),在自己處理完自己的 handler 之后,再把這個(gè) handler 拋出去,供前面的注冊(cè)者處理。

typedef void (*SignalHandler)(int signo, siginfo_t *info, void *context);

static SignalHandler previousSignalHandler = NULL;

+ (void)installSignalHandler {
    struct sigaction old_action;
    sigaction(SIGABRT, NULL, &old_action);
    if (old_action.sa_flags & SA_SIGINFO) {
        previousSignalHandler = old_action.sa_sigaction;
    }

    LDAPMSignalRegister(SIGABRT);
    // .......

}
static void LDAPMSignalRegister(int signal) {
    struct sigaction action;
    action.sa_sigaction = LDAPMSignalHandler;
    action.sa_flags = SA_NODEFER | SA_SIGINFO;
    sigemptyset(&action.sa_mask);
    sigaction(signal, &action, 0);
}
static void LDAPMSignalHandler(int signal, siginfo_t* info, void* context) {
    //  獲取堆棧,收集堆棧
    ........

    LDAPMClearSignalRigister();

    // 處理前者注冊(cè)的 handler
    if (previousSignalHandler) {
        previousSignalHandler(signal, info, context);
    }
}

上面的是一個(gè)處理 Signal handler 沖突的大概代碼思路,下面是 NSException handler 的處理思路,兩者大同小異。

static NSUncaughtExceptionHandler *previousUncaughtExceptionHandler;

static void LDAPMUncaughtExceptionHandler(NSException *exception) {
    // 獲取堆棧,收集堆棧
    // ......
    //  處理前者注冊(cè)的 handler
    if (previousUncaughtExceptionHandler) {
        previousUncaughtExceptionHandler(exception);
    }
}

+ (void)installExceptionHandler {
    previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
    NSSetUncaughtExceptionHandler(&LDAPMUncaughtExceptionHandler);
}

堆棧符號(hào)解析

分為兩種情景討論:

1、Crash收集上報(bào)的堆棧信息解析

這種信息一般還原度比較高,基本上都能給出崩潰的具體定位信息,一些需要解析堆棧符號(hào)的解決方法如下:
以下內(nèi)容來(lái)自文章《iOS異常捕獲-堆棧信息的解析》

異常信息有三種類型:
1.已標(biāo)記錯(cuò)誤位置的:

test 0x000000010bfddd8c -[ViewController viewDidLoad] + 8588

- 這種信息已經(jīng)很明確了,不用解析

2.有模塊地址的情況:

test 0x00000001018157dc 0x100064000 + 24844252

以上面為例子,從左到右依次是:
二進(jìn)制庫(kù)名(test),調(diào)用方法的地址(0x00000001018157dc),模塊地址(0x100064000)+偏移地址(24844252)

3.無(wú)模塊地址的情況:

test 0x00000001018157dc test + 24844252


解析堆棧信息
dSYM符號(hào)表獲取,xcode->window->organizer->右鍵你的應(yīng)用 show finder->右鍵.xcarchive 顯示包內(nèi)容->dSYMs->test.app.dYSM

然后使用atos命令來(lái)符號(hào)化某個(gè)特定模塊加載地址

atos [-arch 架構(gòu)名] [-o 符號(hào)表] [-l 模塊地址] [方法地址]

使用終端,進(jìn)到test.app.dYSM所在目錄

一.如果是有模塊地址的情況,運(yùn)行:

atos -arch arm64 -o test.app.dSYM/Contents/Resources/DWARF/test -l 0x100064000 0x00000001018157dc

二.如果是無(wú)模塊地址的情況

1.先將偏移地址轉(zhuǎn)為16進(jìn)制:

24844252 = 0x17B17DC

2.然后用方法的地址-偏移地址,得到的就是模塊地址

0x00000001018157dc - 0x17B17DC = 0x100064000

3.最后運(yùn)行:

atos -arch arm64 -o test.app.dSYM/Contents/Resources/DWARF/test -l 0x100064000 0x00000001018157dc

2、蘋果自帶Crash日志上報(bào)的堆棧信息解析

以下內(nèi)容來(lái)自文章《iOS Crash 捕獲及堆棧符號(hào)化思路剖析》

有四種常見的方法:
* symbolicatecrash
* mac 下的 atos 工具
* linux 下的 atos 的替代品 atosl
* 通過(guò) dSYM 文件提取地址和符號(hào)的對(duì)應(yīng)關(guān)系,進(jìn)行符號(hào)還原

以上方案都有對(duì)應(yīng)的應(yīng)用場(chǎng)景,對(duì)于線上的 Crash 堆棧符號(hào)還原,主要采用的還是后三種方案。atos 和 atosl 的使用方法很類似,以下是 atos 的一個(gè)示例。

atos -o MonitorExample 0x0000000100062ac4  ARM-64 -l 0x100058000

// 還原結(jié)果
-[GYRootViewController tableView:cellForRowAtIndexPath:] (in GYMonitorExample) (GYRootViewController.m:41)

但是 atos 是Mac上一個(gè)工具,需要使用 Mac 或者黑蘋果來(lái)進(jìn)行解析工作,如果由后臺(tái)來(lái)做解析工作,往往需要一套基于 Linux 的解析方案,這個(gè)時(shí)候可以選擇 atosl,但是這個(gè)庫(kù)已經(jīng)有多年沒(méi)有更新了,同時(shí)基于我司的嘗試, atosl 好像不太支持 arm64 架構(gòu),所以我們放棄了該方案。
最終使用了第四個(gè)方案,提取 dSYM 的符號(hào)表,可以自己研發(fā)工具,也可以直接使用 bugly 和友盟的工具,下面是提取出來(lái)的符號(hào)表。第一列是起始內(nèi)存地址,第二列是結(jié)束地址,第三列是對(duì)應(yīng)的函數(shù)名、文件名以及行號(hào)。

a840    a854    -[GYRootViewController tableView:cellForRowAtIndexPath:] GYRootViewController.m:41
a854    a858    -[GYRootViewController tableView:cellForRowAtIndexPath:] GYRootViewController.m:42
a858    a87c    -[GYRootViewController tableView:cellForRowAtIndexPath:] GYRootViewController.m:42
a87c    a894    -[GYRootViewController tableView:cellForRowAtIndexPath:] GYRootViewController.m:42
a894    a8a0    -[GYRootViewController tableView:cellForRowAtIndexPath:] GYRootViewController.m:42
aa3c    aa80    -[GYFilePreviewViewController initWithFilePath:] GYRootViewController.m:21
aa80    aaa8    -[GYFilePreviewViewController initWithFilePath:] GYFilePreviewViewController.m:23
aaa8    aab8    -[GYFilePreviewViewController initWithFilePath:] GYFilePreviewViewController.m:23
aab8    aabc    -[GYFilePreviewViewController initWithFilePath:] GYFilePreviewViewController.m:24
aabc    aac8    -[GYFilePreviewViewController initWithFilePath:] GYFilePreviewViewController.m:24

因?yàn)槌绦蛎看螁?dòng)基地址都會(huì)變化,所以上面提到的地址是相對(duì)偏移地址,在我們獲取到崩潰堆棧地址后,可以根據(jù)堆棧中的偏移地址來(lái)與符號(hào)表中的地址來(lái)做匹配,進(jìn)而找到堆棧所對(duì)應(yīng)的函數(shù)符號(hào)。比如下面的第四行,偏移為 43072 轉(zhuǎn)換為十六進(jìn)制就是 a840,用 a840 去上面的符號(hào)表中找對(duì)應(yīng)關(guān)系,會(huì)發(fā)現(xiàn)對(duì)應(yīng)著 -[GYRootViewController tableView:cellForRowAtIndexPath:],基于這種方式,就可以將堆棧地址完全還原為函數(shù)符號(hào)啦。

0   libsystem_kernel.dylib              0x0000000186cfd314 0x186cde000 + 127764
1   Foundation                          0x00000001887f5590 0x1886ec000 + 1086864
2   GYMonitorExample                    0x00000001000da4ac 0x1000d0000 + 42156
3   GYMonitorExample                    0x00000001000da840 0x1000d0000 + 43072

野指針Crash的解決方案

野指針是最玄的Crash,表現(xiàn)在飄忽不定(往往不是100%復(fù)現(xiàn))、難以定位(往往崩潰時(shí)的堆棧信息并不能定位到具體的代碼行)、難以消除(往往負(fù)責(zé)的業(yè)務(wù)和代碼邏輯使其隱藏得很深,在測(cè)試的覆蓋范圍外出現(xiàn))。

既然不能根治,那就盡量把它抑制吧,下面是一些優(yōu)秀的文章:

參考文章:

1、iOS異常捕獲
2、iOS Crash文件的解析
3、漫談iOS Crash收集框架
4、iOS開發(fā)質(zhì)量的那些事

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

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