iOS操作系統(tǒng)-- App啟動(dòng)流程分析與優(yōu)化

背景知識(shí):

  • mach-o文件為基于Mach核心的操作系統(tǒng)的可執(zhí)行文件席里、目標(biāo)代碼或動(dòng)態(tài)庫(kù),是.out的代替拢驾,其提供了更強(qiáng)的擴(kuò)展性并提升了符號(hào)表中信息的訪問(wèn)速度奖磁,
  • 符號(hào)表,用于標(biāo)記源代碼中包括標(biāo)識(shí)符独旷、聲明信息、行號(hào)寥裂、函數(shù)名稱等元素的具體信息嵌洼,比如說(shuō)數(shù)據(jù)類(lèi)型、作用域以及內(nèi)存地址封恰,iOS符號(hào)表在dSYM文件中
  • 程序構(gòu)建過(guò)程:編譯分三步走麻养,對(duì)
    源文件進(jìn)行預(yù)處理(processing),處理預(yù)編譯指令诺舔,生成.i文件鳖昌,下一步進(jìn)行編譯备畦,進(jìn)行詞法分析(lex工具識(shí)別詞法規(guī)則語(yǔ)義表)、語(yǔ)法分析和語(yǔ)義分析生成.s匯編文件许昨,最后進(jìn)行匯編懂盐,生成二進(jìn)制目標(biāo)文件.o。目標(biāo)文件再進(jìn)行鏈接器鏈接糕档,形成可執(zhí)行文件.a或mach-o文件莉恼。
  • 鏈接分為動(dòng)態(tài)鏈接和靜態(tài)鏈接,靜態(tài)鏈接會(huì)將所有目標(biāo)文件.o全部?jī)?nèi)容鏈接到執(zhí)行文件中速那,如果另外的執(zhí)行文件需要其中的功能俐银,也必須全部收錄。動(dòng)態(tài)鏈接為了解決這樣的空間浪費(fèi)問(wèn)題端仰,只將函數(shù)信息鏈接加入執(zhí)行文件
  • dyld是加載動(dòng)態(tài)鏈接庫(kù)的庫(kù)捶惜,該庫(kù)在加載可執(zhí)行文件的時(shí)候,遞歸加載所需要的所有動(dòng)態(tài)庫(kù)荔烧。動(dòng)態(tài)庫(kù)包括iOS操作系統(tǒng)的系統(tǒng)framework吱七,oc的runtime系統(tǒng)libobjc,系統(tǒng)級(jí)別的庫(kù)libSystem茴晋,例如libdispatch(GCD)陪捷、libsystem_block(Block)

App啟動(dòng)大致流程

對(duì)于一個(gè)可執(zhí)行文件來(lái)說(shuō),它的加載過(guò)程是:
分為兩大部分:

  1. pre-main 指的是操作系統(tǒng)開(kāi)始執(zhí)行一個(gè)可執(zhí)行文件诺擅,并完成進(jìn)程創(chuàng)建市袖、執(zhí)行文件加載、動(dòng)態(tài)鏈接烁涌、環(huán)境配置
  2. main 指的是從加載main函數(shù)入口以后苍碟,到app delegate完成加載回調(diào)的過(guò)程

操作系統(tǒng)加載App可執(zhí)行文件

操作系統(tǒng)加載可執(zhí)行文件,通過(guò)fork(創(chuàng)建一個(gè)進(jìn)程)指令在新的空間內(nèi)來(lái)執(zhí)行可執(zhí)行文件撮执,加載依賴的可執(zhí)行文件(mach-o)文件微峰,定位其內(nèi)部與外部指針引用,例如字符串與函數(shù)抒钱,執(zhí)行聲明為attribute((constructor))的C函數(shù)蜓肆,加載擴(kuò)展(Category)中的方法,C++靜態(tài)對(duì)象加載谋币,調(diào)用ObjC的+load函數(shù)

image
image

基本流程:

App 開(kāi)始啟動(dòng)后仗扬,系統(tǒng)首先加載可執(zhí)行文件(自身 App 的所有 .o 文件的集合),然后加載動(dòng)態(tài)鏈接器 dyld蕾额,dyld 是一個(gè)專(zhuān)門(mén)用來(lái)加載動(dòng)態(tài)鏈接庫(kù)的庫(kù)早芭。 執(zhí)行從 dyld 開(kāi)始,dyld 從可執(zhí)行文件的依賴開(kāi)始诅蝶,遞歸加載所有的依賴動(dòng)態(tài)鏈接庫(kù)退个。
動(dòng)態(tài)鏈接庫(kù)包括:iOS 中用到的所有系統(tǒng) framework募壕,加載 OC runtime 方法的 libobjc,系統(tǒng)級(jí)別的 libSystem语盈,例如 libdispatch(GCD) 和 libsystem_blocks (Block)舱馅。

dyld加載動(dòng)態(tài)庫(kù)

動(dòng)態(tài)鏈接庫(kù)的加載過(guò)程主要由dyld來(lái)完成,dyld是蘋(píng)果的動(dòng)態(tài)鏈接器黎烈。

image
  1. 系統(tǒng)先讀取App的可執(zhí)行文件(Mach-O文件)里的mach-o headers
  2. dyld去初始化運(yùn)行環(huán)境习柠,從里面獲得動(dòng)態(tài)依賴,開(kāi)啟緩存策略照棋,加載程序相關(guān)依賴庫(kù)(其中也包含我們的可執(zhí)行文件)资溃,并對(duì)這些庫(kù)進(jìn)行鏈接,最后調(diào)用每個(gè)依賴庫(kù)的初始化方法烈炭,在這一步溶锭,runtime被初始化。當(dāng)所有依賴庫(kù)的初始化后,輪到最后一位(程序可執(zhí)行文件)進(jìn)行初始化。
  3. 檢查和確認(rèn)符號(hào)表的是否存在和正確
  4. Map所有mach-o文件兵钮,用來(lái)整體統(tǒng)計(jì)變量聲明、函數(shù)調(diào)用等信息
  5. 進(jìn)行bind操作拱绑,對(duì)從其他庫(kù)的引用的符號(hào)、函數(shù)等丽蝎,進(jìn)行其內(nèi)存地址進(jìn)行修正綁定
  6. 進(jìn)行rebase操作猎拨,對(duì)自身庫(kù)內(nèi)部的引用進(jìn)行修正
  7. 進(jìn)行runtime系統(tǒng)初始化,會(huì)對(duì)項(xiàng)目中所有類(lèi)進(jìn)行類(lèi)結(jié)構(gòu)初始化屠阻,然后調(diào)用所有的load方法红省。
  8. 最后dyld返回main函數(shù)地址,main函數(shù)被調(diào)用国觉,我們便來(lái)到了熟悉的程序入口吧恃。
    當(dāng)加載一個(gè) Mach-O 文件 (一個(gè)可執(zhí)行文件或者一個(gè)庫(kù)) 時(shí),動(dòng)態(tài)鏈接器首先會(huì)檢查共享緩存看看是否存在其中麻诀,如果存在痕寓,那么就直接從共享緩存中拿出來(lái)使用。每一個(gè)進(jìn)程都把這個(gè)共享緩存映射到了自己的地址空間中蝇闭。這個(gè)方法大大優(yōu)化了 OS X 和 iOS 上程序的啟動(dòng)時(shí)間呻率。

Mach-O 鏡像文件

官方文檔:
https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/MachOOverview.htm

Mach-O是OS X中二進(jìn)制文件的本機(jī)可執(zhí)行格式,是傳送代碼的首選格式丁眼】攴铮可執(zhí)行格式確定二進(jìn)制文件中的代碼和數(shù)據(jù)被讀入內(nèi)存的順序昭殉。代碼和數(shù)據(jù)的排序會(huì)影響內(nèi)存使用和分頁(yè)活動(dòng)苞七,從而直接影響程序的性能藐守。段的大小通過(guò)其包含的所有段中的字節(jié)數(shù)來(lái)度量,并向上舍入到下一個(gè)虛擬內(nèi)存頁(yè)邊界蹂风。
Mach-O二進(jìn)制文件被組織成segements卢厂。每個(gè)segement包含一個(gè)或多個(gè)部分。每個(gè)部分都有不同類(lèi)型的代碼或數(shù)據(jù)惠啄。segement始終從頁(yè)面邊界開(kāi)始慎恒,但section不一定是頁(yè)面對(duì)齊的。因此撵渡,segement終是4096字節(jié)或4千字節(jié)的倍數(shù)融柬,其中4096字節(jié)是最小大小。
Mach-O可執(zhí)行文件的segement和section根據(jù)其預(yù)期用途命名趋距。segement名稱的約定是使用以雙下劃線開(kāi)頭的全大寫(xiě)字母(例如粒氧,TEXT); section名稱的約定是使用以雙下劃線開(kāi)頭的全小寫(xiě)字母(例如, text)节腐。
Mach-O可執(zhí)行文件中有幾個(gè)可能的segements外盯,但只有兩個(gè)與性能有關(guān):__TEXT段和__DATA段。

The __TEXT Segment: Read Only
__TEXT segment是包含可執(zhí)行代碼和常量數(shù)據(jù)的只讀區(qū)域翼雀。按照慣例饱苟,編譯器工具創(chuàng)建具有至少一個(gè)只讀__TEXT segment的每個(gè)可執(zhí)行文件。由于該段是只讀的狼渊,因此內(nèi)核可以將__TEXT segment直接從可執(zhí)行文件映射到內(nèi)存中一次箱熬。當(dāng)segment被映射到內(nèi)存時(shí),它可以在所有進(jìn)程之間共享其內(nèi)容囤锉。 (這主要是框架和其他共享庫(kù)的情況坦弟。)只讀屬性還意味著構(gòu)成__TEXT segment的頁(yè)面永遠(yuǎn)不必保存到后備存儲(chǔ)。如果內(nèi)核需要釋放物理內(nèi)存官地,它可以丟棄一個(gè)或多個(gè)__TEXT頁(yè)面酿傍,并在需要時(shí)從磁盤(pán)重新讀取它們。
__TEXT segment的主要部分,sections分布

  • __text 已編譯的可執(zhí)行文件的機(jī)器代碼
  • __const 一般的常量數(shù)據(jù)
  • __cstring 文字字符串常量(源代碼中的引用字符串)
  • __picsymbol_stub 動(dòng)態(tài)鏈接器(dyld)使用的與位置無(wú)關(guān)的代碼存根例程

The __DATA Segment: Read/Write
__DATA segment 包含可執(zhí)行文件的非常量變量驱入。該 segement 是可讀寫(xiě)的赤炒,因?yàn)樗强蓪?xiě)的,所以對(duì)于與庫(kù)鏈接的每個(gè)進(jìn)程亏较,邏輯上復(fù)制靜態(tài)庫(kù)或其他動(dòng)態(tài)共享庫(kù)的__DATA段莺褒。當(dāng)內(nèi)存頁(yè)面可讀寫(xiě)時(shí),內(nèi)核會(huì)使其變?yōu)閏opy-on-write雪情。此技術(shù)可以做到遵岩,動(dòng)態(tài)庫(kù)是在內(nèi)存中共享的,可以被其他各個(gè)進(jìn)程訪問(wèn),但因?yàn)開(kāi)_DATA Segment是可讀可寫(xiě)的尘执,就會(huì)通過(guò)某一進(jìn)程對(duì)共享的_DATA Segment有寫(xiě)操作的時(shí)候舍哄,再進(jìn)行單獨(dú)的_DATA內(nèi)存空間復(fù)制。
__DATA segment 有許多部分誊锭,其中一些僅由動(dòng)態(tài)鏈接器使用表悬。下面 列出了可以出現(xiàn)在__DATA segment 中的一些更重要的部分。有關(guān)段的完整列表丧靡,請(qǐng)參閱Mach-O運(yùn)行時(shí)體系結(jié)構(gòu)蟆沫。

  • __data 初始化的全局變量(例如int a = 1;或static int a = 1;)。
  • __const 需要重定位的常量數(shù)據(jù)(例如温治,char * const p =“foo”;)
  • __bss 未初始化的靜態(tài)變量(例如饭庞,static int a;)。
  • __common 未初始化的外部全局變量(例如熬荆,int a;外部功能塊)但绕。
  • __dyld 占位符部分,由動(dòng)態(tài)鏈接器使用惶看。
  • __la_symbol_ptr lazy符號(hào)指針捏顺。可執(zhí)行文件調(diào)用的每個(gè)未定義函數(shù)的符號(hào)指針纬黎。
  • __nl_symbol_ptr 非lazy符號(hào)指針幅骄。可執(zhí)行文件引用的每個(gè)未定義數(shù)據(jù)符號(hào)的符號(hào)指針本今。

Mach-O 性能影響
Mach-O可執(zhí)行文件的__TEXT segment和__DATA segment的組成與性能有直接關(guān)系拆座。優(yōu)化這些sections的技術(shù)和目的是不同的。但是冠息,它們的共同目標(biāo)是:提高內(nèi)存使用效率挪凑。

最典型的Mach-O的文件由可執(zhí)行代碼組成,在__TEXT逛艰,__text當(dāng)中躏碳。如__TEXT segment,該__TEXT是只讀的散怖,并直接映射到可執(zhí)行文件菇绵,所以如果內(nèi)核需要回收某些__text頁(yè)面占用的物理內(nèi)存,就不必將頁(yè)面保存到back store再將其分頁(yè)镇眷。它只需要釋放內(nèi)存咬最,并在后面代碼引用的時(shí)候從磁盤(pán)重新讀回。雖然這比交換內(nèi)存分頁(yè)的成本低欠动,因?yàn)檫@只是一個(gè)磁盤(pán)訪問(wèn)永乌,而不是兩個(gè)內(nèi)存分頁(yè)的交換 , 但這仍然很損耗性能,特別是如果必須從磁盤(pán)重新創(chuàng)建許多頁(yè)面翅雏。

對(duì)于這種情況的改進(jìn)硝桩,是通過(guò)程序重新排序來(lái)改進(jìn)代碼的引用位置,如改進(jìn)參考位置中所述枚荣。該技術(shù)將方法和功能組合在一起,具體取決于它們的執(zhí)行順序啼肩,調(diào)用頻率以及它們相互調(diào)用的頻率橄妆。如果__text部分組中的頁(yè)面以這種方式邏輯上起作用,則它們不太可能被多次釋放和讀回祈坠。例如害碾,如果將所有啟動(dòng)時(shí)初始化函數(shù)放在一個(gè)或兩個(gè)頁(yè)面上,則在發(fā)生初始化后不必重新創(chuàng)建頁(yè)面赦拘。

與__TEXT段不同慌随,__DATA可以寫(xiě)入段,因此段中的頁(yè)面__DATA不可共享躺同「蟛拢框架中的非常量全局變量可能會(huì)對(duì)性能產(chǎn)生影響,因?yàn)榕c框架鏈接的每個(gè)進(jìn)程都會(huì)獲得這些變量的副本蹋艺。解決這個(gè)問(wèn)題的主要解決辦法是盡可能多的非恒定的全局變量盡可能轉(zhuǎn)移到__TEXT剃袍,__const通過(guò)宣布他們部分const。減少共享內(nèi)存頁(yè)面描述了此技術(shù)和相關(guān)技術(shù)捎谨。這通常不是應(yīng)用程序的問(wèn)題民效,因?yàn)閼?yīng)用程序中的__DATA部分不與其他應(yīng)用程序共享。

編譯器將不同類(lèi)型的非常量全局?jǐn)?shù)據(jù)存儲(chǔ)在段的不同部分中__DATA涛救。這些類(lèi)型的數(shù)據(jù)是未初始化的靜態(tài)數(shù)據(jù)和符號(hào)與未聲明的“暫定定義”的ANSI C概念一致extern畏邢。未初始化的靜態(tài)數(shù)據(jù)位于__bss段的__DATA部分中。暫定的符號(hào)在__common 該__DATA部分检吆。

該 ANSI C和 C ++標(biāo)準(zhǔn)指定系統(tǒng)必須將未初始化的靜態(tài)變量設(shè)置為零舒萎。(未初始化的其他類(lèi)型的未初始化數(shù)據(jù)。)由于未初始化的靜態(tài)變量和臨時(shí)定義符號(hào)存儲(chǔ)在單獨(dú)的部分中蹭沛,因此系統(tǒng)需要以不同方式對(duì)待它們逆甜。但是當(dāng)變量位于不同的部分時(shí),它們更有可能最終出現(xiàn)在不同的內(nèi)存頁(yè)面上致板,因此可以單獨(dú)進(jìn)行交換交煞,從而使代碼運(yùn)行速度變慢。如減少共享內(nèi)存頁(yè)面中所述斟或,這些問(wèn)題的解決方案是在段的一個(gè)部分中合并非常量全局?jǐn)?shù)據(jù)__DATA素征。

ObjC Runtime

dyld的加載過(guò)程會(huì)初始化Runtime系統(tǒng),在此階段,有相當(dāng)多的優(yōu)化工作可以做

image

這過(guò)程包括:

  1. 所有類(lèi)型的定義和注冊(cè)御毅,Objective-C的類(lèi)不是編譯器決定的根欧,是運(yùn)行時(shí)動(dòng)態(tài)載入到全局表中的
  2. 非脆弱的ivars變量抵消更新,修改實(shí)例變量的內(nèi)存地址偏移問(wèn)題
  3. 分類(lèi)替換并添加到方法列表中端蛆,將分類(lèi)中的方法加載到方法列表中
  4. 確認(rèn)選擇器全局唯一

Initializers 階段

在Runtime系統(tǒng)加載以后凤粗,開(kāi)始進(jìn)行初始化

image
  1. Objc的+load()函數(shù)
  2. C++的構(gòu)造函數(shù)屬性函數(shù) 形如attribute((constructor)) void DoSomeInitializationWork()
  3. 非基本類(lèi)型的C++靜態(tài)全局變量的創(chuàng)建(通常是類(lèi)或結(jié)構(gòu)體)(non-trivial initializer) 比如一個(gè)全局靜態(tài)結(jié)構(gòu)體的構(gòu)建,如果在構(gòu)造函數(shù)中有繁重的工作今豆,那么會(huì)拖慢啟動(dòng)速度

pre-main階段分析

從上面可以得出以下幾個(gè)結(jié)論嫌拣,影響該階段啟動(dòng)時(shí)間的因素如下:

  1. Mach-O可執(zhí)行文件的加載和內(nèi)存重新分配規(guī)劃,對(duì)于其segment和section進(jìn)行虛擬內(nèi)存的分頁(yè)管理的調(diào)度
  2. dyld動(dòng)態(tài)鏈接內(nèi)存中的公共鏡像呆躲,在運(yùn)行時(shí)進(jìn)行檢查共享數(shù)據(jù)和鏈接調(diào)用
  3. Runtime的初始化异逐,包括class注冊(cè)、category加載插掂、變量對(duì)齊等
  4. C++靜態(tài)對(duì)象和全局變量的加載
  5. ObjeC所有l(wèi)oad函數(shù)的調(diào)用加載

優(yōu)化措施:

  1. 減少ObjC的類(lèi)膨脹問(wèn)題灰瞻,清理沒(méi)有使用的類(lèi),合并松散無(wú)用的類(lèi)
  2. 減少靜態(tài)變量的聲明和初始化的分離
static int x;
static short conv_table [128];
//更換為
static int x = 0;
static short conv_table [128] = {0};

減少靜態(tài)變量的使用

  1. 減少符號(hào)表的導(dǎo)出
    通過(guò)設(shè)置-exported_symbols_list或-unexported_symbols_lis來(lái)限制符號(hào)表的導(dǎo)出辅甥,從而減少dyld的工作量
  2. 去除沒(méi)有使用的動(dòng)態(tài)庫(kù)依賴酝润,明確所依賴的frameworks是require還是optional,optional會(huì)動(dòng)態(tài)進(jìn)行額外檢查
  3. 刪除沒(méi)有用的方法
  4. 減少+load函數(shù)的實(shí)現(xiàn)璃弄,并減少在其中操作的邏輯
  5. 對(duì)某些經(jīng)常調(diào)用的代碼進(jìn)行二進(jìn)制化袍祖,生成靜態(tài)庫(kù),多使用靜態(tài)庫(kù)代替動(dòng)態(tài)庫(kù)谢揪,將多個(gè)靜態(tài)庫(kù)框架蕉陋,集中制作成靜態(tài)framework,從而能夠減少dyld的鏈接工作
    關(guān)于冷啟動(dòng)和熱啟動(dòng)的不同如下:
image

main階段

image

從上圖可以得到拨扶,影響main階段的啟動(dòng)時(shí)間因素是:

  1. AppDelegate代理的加載生命周期回調(diào)
  2. Application Window的布局凳鬓、繪制和加載
  3. RootViewController的加載
    優(yōu)化點(diǎn):
  4. 壓縮和減小啟動(dòng)圖片
  5. 盡量不使用storyboard或者是nib來(lái)布局rootViewController
  6. 在didFinishLaunchingWithOptions階段,盡可能減少阻塞代碼的執(zhí)行患民,可以利用多線程進(jìn)行加載邏輯的處理缩举,注意多線程對(duì)主線程同步阻塞可能造成的黑屏問(wèn)題
  7. 將非同步需求的初始化邏輯進(jì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)離奇詭異,居然都是意外死亡赦肃,警方通過(guò)查閱死者的電腦和手機(jī)溅蛉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)公浪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人船侧,你說(shuō)我怎么就攤上這事欠气。” “怎么了镜撩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵预柒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我袁梗,道長(zhǎng)宜鸯,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任围段,我火速辦了婚禮,結(jié)果婚禮上投放,老公的妹妹穿的比我還像新娘奈泪。我一直安慰自己,他們只是感情好灸芳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布涝桅。 她就那樣靜靜地躺著,像睡著了一般烙样。 火紅的嫁衣襯著肌膚如雪冯遂。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天谒获,我揣著相機(jī)與錄音蛤肌,去河邊找鬼。 笑死批狱,一個(gè)胖子當(dāng)著我的面吹牛裸准,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赔硫,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼炒俱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了爪膊?” 一聲冷哼從身側(cè)響起权悟,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎推盛,沒(méi)想到半個(gè)月后峦阁,有當(dāng)?shù)厝嗽跇?shù)林里發(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
  • 文/蒙蒙 一溜歪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧许蓖,春花似錦蝴猪、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至米酬,卻和暖如春沛豌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赃额。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工加派, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人跳芳。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓哼丈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親筛严。 傳聞我的和親對(duì)象是個(gè)殘疾皇子醉旦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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