【性能優(yōu)化】今日頭條iOS客戶端啟動(dòng)速度優(yōu)化

應(yīng)用啟動(dòng)時(shí)間,直接影響用戶對(duì)一款應(yīng)用的判斷和使用體驗(yàn)。頭條主app本身就包含非常多并且復(fù)雜度高的業(yè)務(wù)模塊(如新聞、視頻等)题画,也接入了很多第三方的插件,這勢(shì)必會(huì)拖慢應(yīng)用的啟動(dòng)時(shí)間德频,本著精益求精的態(tài)度和對(duì)用戶體驗(yàn)的追求苍息,我們希望在業(yè)務(wù)擴(kuò)張的同時(shí)最大程度的優(yōu)化啟動(dòng)時(shí)間。

一抱婉、技術(shù)調(diào)研

先說(shuō)結(jié)論档叔,t(App總啟動(dòng)時(shí)間) = t1(main()之前的加載時(shí)間) + t2(main()之后的加載時(shí)間)。 t1 = 系統(tǒng)dylib(動(dòng)態(tài)鏈接庫(kù))和自身App可執(zhí)行文件的加載蒸绩;
t2 = main方法執(zhí)行之后到AppDelegate類中的- (BOOL)Application:(UIApplication *)Application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法執(zhí)行結(jié)束前這段時(shí)間,主要是構(gòu)建第一個(gè)界面铃肯,并完成渲染展示患亿。

main()調(diào)用之前的加載過(guò)程

App開(kāi)始啟動(dòng)后,系統(tǒng)首先加載可執(zhí)行文件(自身App的所有.o文件的集合),然后加載動(dòng)態(tài)鏈接庫(kù)dyld步藕,dyld是一個(gè)專門(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)灾搏。

其實(shí)無(wú)論對(duì)于系統(tǒng)的動(dòng)態(tài)鏈接庫(kù)還是對(duì)于App本身的可執(zhí)行文件而言,他們都算是image(鏡像)立润,而每個(gè)App都是以image(鏡像)為單位進(jìn)行加載的狂窑,那么image究竟包括哪些呢?

什么是image

1.executable可執(zhí)行文件 比如.o文件桑腮。
2.dylib 動(dòng)態(tài)鏈接庫(kù) framework就是動(dòng)態(tài)鏈接庫(kù)和相應(yīng)資源包含在一起的一個(gè)文件夾結(jié)構(gòu)泉哈。
3.bundle 資源文件 只能用dlopen加載,不推薦使用這種方式加載破讨。

除了我們App本身的可行性文件丛晦,系統(tǒng)中所有的framework比如UIKit、Foundation等都是以動(dòng)態(tài)鏈接庫(kù)的方式集成進(jìn)App中的提陶。

系統(tǒng)使用動(dòng)態(tài)鏈接有幾點(diǎn)好處:

代碼共用:很多程序都動(dòng)態(tài)鏈接了這些 lib采呐,但它們?cè)趦?nèi)存和磁盤(pán)中中只有一份。 易于維護(hù):由于被依賴的 lib 是程序執(zhí)行時(shí)才鏈接的搁骑,所以這些 lib 很容易做更新斧吐,比如libSystem.dylib 是 libSystem.B.dylib 的替身,哪天想升級(jí)直接換成libSystem.C.dylib 然后再替換替身就行了仲器。 減少可執(zhí)行文件體積:相比靜態(tài)鏈接煤率,動(dòng)態(tài)鏈接在編譯時(shí)不需要打進(jìn)去,所以可執(zhí)行文件的體積要小很多乏冀。

image

如上圖所示蝶糯,不同進(jìn)程之間共用系統(tǒng)dylib的_TEXT區(qū),但是各自維護(hù)對(duì)應(yīng)的_DATA區(qū)辆沦。

所有動(dòng)態(tài)鏈接庫(kù)和我們App中的靜態(tài)庫(kù).a和所有類文件編譯后的.o文件最終都是由dyld(the dynamic link editor)昼捍,Apple的動(dòng)態(tài)鏈接器來(lái)加載到內(nèi)存中。每個(gè)image都是由一個(gè)叫做ImageLoader的類來(lái)負(fù)責(zé)加載(一一對(duì)應(yīng))肢扯,那么ImageLoader又是什么呢妒茬?

什么是ImageLoader

image 表示一個(gè)二進(jìn)制文件(可執(zhí)行文件或 so 文件),里面是被編譯過(guò)的符號(hào)蔚晨、代碼等乍钻,所以 ImageLoader 作用是將這些文件加載進(jìn)內(nèi)存肛循,且每一個(gè)文件對(duì)應(yīng)一個(gè)ImageLoader實(shí)例來(lái)負(fù)責(zé)加載。
兩步走: 在程序運(yùn)行時(shí)它先將動(dòng)態(tài)鏈接的 image 遞歸加載 (也就是上面測(cè)試棧中一串的遞歸調(diào)用的時(shí)刻)银择。 再?gòu)目蓤?zhí)行文件 image 遞歸加載所有符號(hào)多糠。

當(dāng)然所有這些都發(fā)生在我們真正的main函數(shù)執(zhí)行前。

動(dòng)態(tài)鏈接庫(kù)加載的具體流程

動(dòng)態(tài)鏈接庫(kù)的加載步驟具體分為5步:

  1. load dylibs image 讀取庫(kù)鏡像文件

  2. Rebase image

  3. Bind image

  4. Objc setup

  5. initializers

load dylibs image

在每個(gè)動(dòng)態(tài)庫(kù)的加載過(guò)程中浩考, dyld需要:

  1. 分析所依賴的動(dòng)態(tài)庫(kù)

  2. 找到動(dòng)態(tài)庫(kù)的mach-o文件

  3. 打開(kāi)文件

  4. 驗(yàn)證文件

  5. 在系統(tǒng)核心注冊(cè)文件簽名

  6. 對(duì)動(dòng)態(tài)庫(kù)的每一個(gè)segment調(diào)用mmap()

通常的夹孔,一個(gè)App需要加載100到400個(gè)dylibs, 但是其中的系統(tǒng)庫(kù)被優(yōu)化析孽,可以很快的加載搭伤。 針對(duì)這一步驟的優(yōu)化有:

  1. 減少非系統(tǒng)庫(kù)的依賴

  2. 合并非系統(tǒng)庫(kù)

  3. 使用靜態(tài)資源,比如把代碼加入主程序

rebase/bind

由于ASLR(address space layout randomization)的存在绿淋,可執(zhí)行文件和動(dòng)態(tài)鏈接庫(kù)在虛擬內(nèi)存中的加載地址每次啟動(dòng)都不固定闷畸,所以需要這2步來(lái)修復(fù)鏡像中的資源指針,來(lái)指向正確的地址吞滞。 rebase修復(fù)的是指向當(dāng)前鏡像內(nèi)部的資源指針佑菩; 而bind指向的是鏡像外部的資源指針。
rebase步驟先進(jìn)行裁赠,需要把鏡像讀入內(nèi)存殿漠,并以page為單位進(jìn)行加密驗(yàn)證,保證不會(huì)被篡改佩捞,所以這一步的瓶頸在IO绞幌。bind在其后進(jìn)行,由于要查詢符號(hào)表一忱,來(lái)指向跨鏡像的資源莲蜘,加上在rebase階段,鏡像已被讀入和加密驗(yàn)證帘营,所以這一步的瓶頸在于CPU計(jì)算票渠。
通過(guò)命令行可以查看相關(guān)的資源指針:

xcrun dyldinfo -rebase -bind -lazy_bind myApp.App/myApp

優(yōu)化該階段的關(guān)鍵在于減少__DATA segment中的指針數(shù)量。我們可以優(yōu)化的點(diǎn)有:

  1. 減少Objc類數(shù)量芬迄, 減少selector數(shù)量

  2. 減少C++虛函數(shù)數(shù)量

  3. 轉(zhuǎn)而使用swift stuct(其實(shí)本質(zhì)上就是為了減少符號(hào)的數(shù)量)

Objc setup

這一步主要工作是:

  1. 注冊(cè)O(shè)bjc類 (class registration)

  2. 把category的定義插入方法列表 (category registration)

  3. 保證每一個(gè)selector唯一 (selctor uniquing)

由于之前2步驟的優(yōu)化问顷,這一步實(shí)際上沒(méi)有什么可做的。

initializers

以上三步屬于靜態(tài)調(diào)整(fix-up)禀梳,都是在修改__DATA segment中的內(nèi)容杜窄,而這里則開(kāi)始動(dòng)態(tài)調(diào)整,開(kāi)始在堆和堆棧中寫(xiě)入內(nèi)容算途。 在這里的工作有:

  1. Objc的+load()函數(shù)

  2. C++的構(gòu)造函數(shù)屬性函數(shù) 形如attribute((constructor)) void DoSomeInitializationWork()

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

Objc的load函數(shù)和C++的靜態(tài)構(gòu)造函數(shù)采用由底向上的方式執(zhí)行郊艘,來(lái)保證每個(gè)執(zhí)行的方法荷科,都可以找到所依賴的動(dòng)態(tài)庫(kù)唯咬。

image

上圖是在自定義的類XXViewController的+load方法斷點(diǎn)的調(diào)用堆棧纱注,清楚的看到整個(gè)調(diào)用棧和順序:

  1. dyld 開(kāi)始將程序二進(jìn)制文件初始化

  2. 交由 ImageLoader 讀取 image畏浆,其中包含了我們的類、方法等各種符號(hào)

  3. 由于 runtime 向 dyld 綁定了回調(diào)狞贱,當(dāng) image 加載到內(nèi)存后刻获,dyld 會(huì)通知 runtime 進(jìn)行處理

  4. runtime 接手后調(diào)用 mapimages 做解析和處理,接下來(lái) loadimages 中調(diào)用 callloadmethods 方法瞎嬉,遍歷所有加載進(jìn)來(lái)的 Class蝎毡,按繼承層級(jí)依次調(diào)用 Class 的 +load 方法和其 Category 的 +load 方法

至此,可執(zhí)行文件中和動(dòng)態(tài)庫(kù)所有的符號(hào)(Class氧枣,Protocol沐兵,Selector,IMP便监,…)都已經(jīng)按格式成功加載到內(nèi)存中扎谎,被 runtime 所管理,再這之后烧董,runtime 的那些方法(動(dòng)態(tài)添加 Class毁靶、swizzle 等等才能生效)。

整個(gè)事件由 dyld 主導(dǎo)逊移,完成運(yùn)行環(huán)境的初始化后预吆,配合 ImageLoader 將二進(jìn)制文件按格式加載到內(nèi)存, 動(dòng)態(tài)鏈接依賴庫(kù)胳泉,并由 runtime 負(fù)責(zé)加載成 objc 定義的結(jié)構(gòu)拐叉,所有初始化工作結(jié)束后,dyld 調(diào)用真正的 main 函數(shù)扇商。

如果程序剛剛被運(yùn)行過(guò)凤瘦,那么程序的代碼會(huì)被dyld緩存,因此即使殺掉進(jìn)程再次重啟加載時(shí)間也會(huì)相對(duì)快一點(diǎn)钳吟,如果長(zhǎng)時(shí)間沒(méi)有啟動(dòng)或者當(dāng)前dyld的緩存已經(jīng)被其他應(yīng)用占據(jù)廷粒,那么這次啟動(dòng)所花費(fèi)的時(shí)間就要長(zhǎng)一點(diǎn),這就分別是熱啟動(dòng)和冷啟動(dòng)的概念红且,如下圖所示:

image

main()之前的加載時(shí)間如何衡量

那么問(wèn)題就來(lái)了坝茎,那怎么衡量main()之前也就是time1的耗時(shí)呢,蘋(píng)果官方提供了一種方法暇番,那就是在真機(jī)調(diào)試的時(shí)候勾選dyldPRINTSTATISTICS選項(xiàng)嗤放。

image

會(huì)得到如下形式的輸出:

image

由此可見(jiàn)對(duì)于系統(tǒng)級(jí)別的動(dòng)態(tài)鏈接庫(kù),因?yàn)樘O(píng)果做了優(yōu)化壁酬,所以耗時(shí)并不多次酌,在這個(gè)awesome的例子中恨课,自身App中的代碼占用了整體時(shí)間的94.2% 我們應(yīng)用中一次典型的Log如下:

image

由此可見(jiàn),最多的用時(shí)還是在image加載和OC類的初始化岳服,共占用總時(shí)長(zhǎng)的79.3%剂公,精簡(jiǎn)framework的引入和OC類有優(yōu)化的空間。

總結(jié)一下:對(duì)于main()調(diào)用之前的耗時(shí)我們可以優(yōu)化的點(diǎn)有:

  1. 減少不必要的framework吊宋,因?yàn)閯?dòng)態(tài)鏈接比較耗時(shí)

  2. check framework應(yīng)當(dāng)設(shè)為optional和required纲辽,如果該framework在當(dāng)前App支持的所有iOS系統(tǒng)版本都存在,那么就設(shè)為required璃搜,否則就設(shè)為optional拖吼,因?yàn)閛ptional會(huì)有些額外的檢查

  3. 合并或者刪減一些OC類,關(guān)于清理項(xiàng)目中沒(méi)用到的類这吻,使用工具AppCode代碼檢查功能吊档,查到當(dāng)前項(xiàng)目中沒(méi)有用到的類如下:

    image
  4. 刪減一些無(wú)用的靜態(tài)變量

  5. 刪減沒(méi)有被調(diào)用到或者已經(jīng)廢棄的方法

    方法見(jiàn):http://stackoverflow.com/questions/35233564/how-to-find-unused-code-in-xcode-7
    https://developer.Apple.com/library/ios/documentation/ToolsLanguages/Conceptual/Xcode_Overview/CheckingCodeCoverage.html
    
  6. 將不必須在+load方法中做的事情延遲到+initialize中

  7. 盡量不要用C++虛函數(shù)(創(chuàng)建虛函數(shù)表有開(kāi)銷(xiāo))

main()調(diào)用之后的加載時(shí)間

在main()被調(diào)用之后,App的主要工作就是初始化必要的服務(wù)唾糯,顯示首頁(yè)內(nèi)容等怠硼。而我們的優(yōu)化也是圍繞如何能夠快速展現(xiàn)首頁(yè)來(lái)開(kāi)展。 App通常在AppDelegate類中的- (BOOL)Application:(UIApplication *)Application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中創(chuàng)建首頁(yè)需要展示的view趾断,然后在當(dāng)前runloop的末尾拒名,主動(dòng)調(diào)用CA::Transaction::commit完成視圖的渲染。
而視圖的渲染主要涉及三個(gè)階段:

  1. 準(zhǔn)備階段 這里主要是圖片的解碼

  2. 布局階段 首頁(yè)所有UIView的- (void)layoutSubViews()運(yùn)行

  3. 繪制階段 首頁(yè)所有UIView的- (void)drawRect:(CGRect)rect運(yùn)行
    再加上啟動(dòng)之后必要服務(wù)的啟動(dòng)芋酌、必要數(shù)據(jù)的創(chuàng)建和讀取增显,這些就是我們可以嘗試優(yōu)化的地方

因此,對(duì)于main()函數(shù)調(diào)用之前我們可以優(yōu)化的點(diǎn)有:

  1. 不使用xib脐帝,直接視用代碼加載首頁(yè)視圖

  2. NSUserDefaults實(shí)際上是在Library文件夾下會(huì)生產(chǎn)一個(gè)plist文件同云,如果文件太大的話一次能讀取到內(nèi)存中可能很耗時(shí),這個(gè)影響需要評(píng)估堵腹,如果耗時(shí)很大的話需要拆分(需考慮老版本覆蓋安裝兼容問(wèn)題)

  3. 每次用NSLog方式打印會(huì)隱式的創(chuàng)建一個(gè)Calendar炸站,因此需要?jiǎng)h減啟動(dòng)時(shí)各業(yè)務(wù)方打的log,或者僅僅針對(duì)內(nèi)測(cè)版輸出log

  4. 梳理應(yīng)用啟動(dòng)時(shí)發(fā)送的所有網(wǎng)絡(luò)請(qǐng)求疚顷,是否可以統(tǒng)一在異步線程請(qǐng)求

二旱易、實(shí)測(cè)數(shù)據(jù)

建立了一個(gè)空的HelloWorld工程,只加入了pods中的代碼腿堤,不包含主端的業(yè)務(wù)邏輯代碼阀坏,一次典型的冷啟動(dòng)基本接近2s iPhone6 iOS9.3.5系統(tǒng)測(cè)試主要時(shí)間在加載動(dòng)態(tài)庫(kù),類/方法的初始化還有符號(hào)地址綁定階段笆檀。

image

一次典型的熱啟動(dòng)數(shù)據(jù)如下:可以看到因?yàn)橄到y(tǒng)做了緩存方面的優(yōu)化忌堂,比冷啟動(dòng)快了500ms加上頭條主端業(yè)務(wù)邏輯代碼之后一次典型的熱啟動(dòng)耗時(shí)2.1s。

image

以上用時(shí)均為main()之前的加載耗時(shí)酗洒。

main()函數(shù)之后加載時(shí)間優(yōu)化記錄

NSUserDefaults是否是瓶頸

蘋(píng)果官方文檔提到NSUserDefaults加載的時(shí)候是整個(gè)plist配置文件全部load到內(nèi)存中士修,目前頭條主端當(dāng)中NSUserDefaults存儲(chǔ)了200多項(xiàng)緩存數(shù)據(jù)枷遂,因此懷疑可能拖慢啟動(dòng)速度,但是測(cè)試結(jié)果顯示并不會(huì)棋嘲。 通過(guò)符號(hào)斷點(diǎn)+[NSUserDefaults standardUserDefaults]確定最早一次的+load()從執(zhí)行到結(jié)束耗時(shí)1.8ms酒唉,可見(jiàn)NSUserDefaults的初始化僅耗時(shí)1.8ms,并不是啟動(dòng)耗時(shí)的瓶頸封字。

如何找到拖慢啟動(dòng)應(yīng)用時(shí)長(zhǎng)的瓶頸

為了找到瓶頸黔州,我們?cè)趩?dòng)之后的didFinishLauhcning方法開(kāi)始執(zhí)行到首頁(yè)列表頁(yè)的NewsListViewController的viewDidAppear方法耍鬓,幾乎每個(gè)可能比較耗時(shí)的流程進(jìn)行拆分和統(tǒng)計(jì)阔籽,得到統(tǒng)計(jì)數(shù)據(jù)之后發(fā)現(xiàn): 主要耗時(shí)在首頁(yè)UI構(gòu)造和渲染(storyboard加載,tabBar/topBar渲染牲蜀,開(kāi)屏廣告加載/cell注冊(cè)/日志模塊初始化這幾個(gè)步驟)笆制。

具體優(yōu)化點(diǎn)

因此,針對(duì)于今日頭條這個(gè)App我們可以優(yōu)化的點(diǎn)如下:

  1. 純代碼方式而不是storyboard加載首頁(yè)UI涣达。

  2. 對(duì)didFinishLaunching里的函數(shù)考慮能否挖掘可以延遲加載或者懶加載在辆,需要與各個(gè)業(yè)務(wù)方pm和rd共同check 對(duì)于一些已經(jīng)下線的業(yè)務(wù),刪減冗余代碼度苔。
    對(duì)于一些與UI展示無(wú)關(guān)的業(yè)務(wù)匆篓,如微博認(rèn)證過(guò)期檢查、圖片最大緩存空間設(shè)置等做延遲加載

  3. 對(duì)實(shí)現(xiàn)了+load()方法的類進(jìn)行分析寇窑,盡量將load里的代碼延后調(diào)用鸦概。

  4. 上面統(tǒng)計(jì)數(shù)據(jù)顯示展示feed的導(dǎo)航控制器頁(yè)面(NewsListViewController)比較耗時(shí),對(duì)于viewDidLoad以及viewWillAppear方法中盡量去嘗試少做甩骏,晚做窗市,不做。

優(yōu)化結(jié)果

之前曾經(jīng)有一位同事已經(jīng)做了一定的優(yōu)化饮笛,比如啟動(dòng)之后展示閃屏廣告圖的同時(shí)初始化首頁(yè)的列表頁(yè)咨察,當(dāng)廣告展示完成之后列表頁(yè)也就渲染完成了。經(jīng)過(guò)這一次優(yōu)化之后的main()之后的啟動(dòng)總時(shí)長(zhǎng)通過(guò)上線之后收集數(shù)據(jù)的驗(yàn)證達(dá)到了預(yù)期的效果福青。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末摄狱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子无午,更是在濱河造成了極大的恐慌媒役,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件指厌,死亡現(xiàn)場(chǎng)離奇詭異刊愚,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)踩验,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)鸥诽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)商玫,“玉大人,你說(shuō)我怎么就攤上這事牡借∪” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵钠龙,是天一觀的道長(zhǎng)炬藤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)碴里,這世上最難降的妖魔是什么沈矿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮咬腋,結(jié)果婚禮上羹膳,老公的妹妹穿的比我還像新娘。我一直安慰自己根竿,他們只是感情好陵像,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著寇壳,像睡著了一般醒颖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上壳炎,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天泞歉,我揣著相機(jī)與錄音,去河邊找鬼冕广。 笑死疏日,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撒汉。 我是一名探鬼主播沟优,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼睬辐!你這毒婦竟也來(lái)了挠阁?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤溯饵,失蹤者是張志新(化名)和其女友劉穎侵俗,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體丰刊,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隘谣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寻歧。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掌栅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出码泛,到底是詐尸還是另有隱情猾封,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布噪珊,位于F島的核電站晌缘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏痢站。R本人自食惡果不足惜磷箕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瑟押。 院中可真熱鬧搀捷,春花似錦、人聲如沸多望。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)怀偷。三九已至,卻和暖如春播玖,著一層夾襖步出監(jiān)牢的瞬間椎工,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蜀踏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留维蒙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓果覆,卻偏偏與公主長(zhǎng)得像颅痊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子局待,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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