iOS 編譯與鏈接一:編譯的過程

一:編譯器

編譯器是什么已不用多說,一句話從代碼到機(jī)器碼就是編譯器的工作.


編譯器的架構(gòu)

左邊輸入源碼,右邊輸出機(jī)器碼

Frontend表示前端,主要負(fù)責(zé)詞法分析鞋怀、語法分析、語義分析命锄、生成中間代碼.這時(shí)就會(huì)進(jìn)行各種檢查帮哈,會(huì)報(bào)錯(cuò)或者警告.
Optimizer表示優(yōu)化器,負(fù)責(zé)中間代碼的優(yōu)化,去除冗余代碼,優(yōu)化結(jié)構(gòu)
Backend表示后端,生成機(jī)器碼,并且進(jìn)行鏈接,也就是將不同的二進(jìn)制文件合并成一個(gè)可執(zhí)行文件.

1.LLVM
Xcode5之后完全使用LLVM作為編譯器.

LLVM的架構(gòu)

LLVM也是上面說的那種Frontend -> Optimizer ->Backend架構(gòu).
不過LLVM路子很野,可以有很多個(gè)接口,也就是前端(Frontend),每一種前端對(duì)應(yīng)一種或多種語言,這些前端最終都會(huì)生成相同的中間代碼,叫做LLVM IR;
優(yōu)化器的從始至終只處理LLVM IR.
新增一個(gè)前端不需要對(duì)LLVM的優(yōu)化器進(jìn)行調(diào)整,只需要新增一個(gè)前端; 增加一個(gè)新的平臺(tái)只需要增加一個(gè)后端即可.
相比較而言,GCC就支持一個(gè)新的前端或者后端就要麻煩的多,原本的GCC家族(C,C++,OC),以及Java伴找、.NET芒粹、Python兄纺、Ruby等都可以使用LLVM編譯.

LLVM IR有3種表示形式,
存在內(nèi)存中.
存在硬盤中的.ll代碼文件,可以閱讀.
存在硬盤中的二進(jìn)制文件,擴(kuò)展名是.bc,也就是bitcode.

2.Clang
Clang就是一個(gè)LLVM前端,負(fù)責(zé)將C,C++,OC翻譯成LLVM IR.

Clang的工作內(nèi)容:
預(yù)處理, 去掉注釋,頭文件的引用關(guān)系,把宏定義對(duì)應(yīng)到各個(gè)位置
靜態(tài)分析,給出錯(cuò)誤信息,警告信息和修復(fù)方案
詞法分析,這里會(huì)把代碼切成一個(gè)個(gè) Token,括號(hào),符號(hào),關(guān)鍵字等等都被切割出來
語法分析,驗(yàn)證語法是否正確,將所有節(jié)點(diǎn)組成抽象語法樹AST
生成 LLVM IR, CodeGen會(huì)負(fù)責(zé)將語法樹自頂向下遍歷逐步翻譯成 LLVM IR

3.Swift
同樣LLVM中還需要一個(gè)前端負(fù)責(zé)對(duì) Swift 源代碼進(jìn)行靜態(tài)分析和糾錯(cuò),并轉(zhuǎn)換為 LLVM IR,這個(gè)前端也叫swift.
不過swift比clang的過程要復(fù)雜一些,多了一個(gè)生成SIL的過程.

Swift的工作內(nèi)容:
導(dǎo)入Clang模塊并將它們導(dǎo)出的OC API 映射到相應(yīng)的 Swift API
解析生成AST
生成SIL,將經(jīng)過類型檢查的 AST 降級(jí)為 SIL
優(yōu)化SIL,為程序執(zhí)行額外的高級(jí) Swift 特定優(yōu)化化漆,包括自動(dòng)引用計(jì)數(shù)優(yōu)化估脆,虛擬化和通用專業(yè)化
將 SIL 降級(jí)到 LLVM IR

二.編譯流程

引用戴銘老師的例子走一遍,原文

1.編譯一個(gè)main.m
裝了Xcode就自帶LLVM,可以直接試一試.
創(chuàng)建一個(gè)項(xiàng)目,覆蓋main.m的代碼

#import <Foundation/Foundation.h>
#define DEFINEEight 8

#pragma 這是標(biāo)記
//這是注釋
int main(){
    @autoreleasepool {
        int eight = DEFINEEight;
        int six = 6;
        NSString* site = [[NSString alloc] initWithUTF8String:"starming"];
        int rank = eight + six;
        NSLog(@"%@ rank %d", site, rank);
    }
    return 0;
}

運(yùn)行:

clang -ccc-print-phases main.m

輸出:

               +- 0: input, "main.m", objective-c
            +- 1: preprocessor, {0}, objective-c-cpp-output
         +- 2: compiler, {1}, ir
      +- 3: backend, {2}, assembler
   +- 4: assembler, {3}, object
+- 5: linker, {4}, image
6: bind-arch, "arm64", {5}, image

第0步引入文件
第1步預(yù)編譯,輸出c++文件
第2步編譯為L(zhǎng)LVM IR文件
第3步輸出匯編文件
第4步輸出二進(jìn)制文件
第5步鏈接各二進(jìn)制文件
第6步根據(jù)架構(gòu)輸出對(duì)應(yīng)可執(zhí)行文件

執(zhí)行:

clang -E main.m

輸出了非常多的東西,因?yàn)閷?dǎo)入了foundation,先看最后幾行

# 1 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/FoundationLegacySwiftCompatibility.h" 1 3
# 187 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" 2 3
# 22 "main.m" 2


#pragma 這是標(biāo)記

int main(){
    @autoreleasepool {
        int eight = 8;
        int six = 6;
        NSString* site = [[NSString alloc] initWithUTF8String:"starming"];
        int rank = eight + six;
        NSLog(@"%@ rank %d", site, rank);
    }
    return 0;
}

除了foundation,還可以看到DEFINEEight被替換成了8,注釋沒了,但是#pragma還在
所以預(yù)編譯就做了這些事:導(dǎo)入文件,去除注釋,替換宏定義.

這一步會(huì)生成main.cpp文件,在main.m的同一路徑.幾萬行代碼,在最后可以找到替換為C++的main函數(shù)

int main(){
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int eight = 8;
        int six = 6;
        NSString* site = ((NSString * _Nullable (*)(id, SEL, const char * _Nonnull))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("alloc")), sel_registerName("initWithUTF8String:"), (const char *)"starming");
        int rank = eight + six;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_y4_681pp9bd3c31j1_m0jqnt4h00000gn_T_main_3eda9c_mi_0, site, rank);
    }
    return 0;
}

接下來是詞法分析
執(zhí)行:

clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m

輸出

annot_module_include '#import <Foundation/Foundation.h>
#define DEFINEEight 8

#pragma 這是標(biāo)記
//這是注釋
int main(){
    @autoreleasepool {
        int eight = DEFINEEight;
        int six = 6;
        NSString* site = [[NSString alloc] initWithUTF8String:"starming"];
        int rank = eight + six;
        NSLog(@"%@ rank %d", site, rank);
    }
    return 0;
}
@???W?\?V?A?`'      Loc=<main.m:21:1>
int 'int'    [StartOfLine]  Loc=<main.m:26:1>
identifier 'main'    [LeadingSpace] Loc=<main.m:26:5>
l_paren '('     Loc=<main.m:26:9>
r_paren ')'     Loc=<main.m:26:10>
l_brace '{'     Loc=<main.m:26:11>
at '@'   [StartOfLine] [LeadingSpace]   Loc=<main.m:27:5>
identifier 'autoreleasepool'        Loc=<main.m:27:6>
l_brace '{'  [LeadingSpace] Loc=<main.m:27:22>
int 'int'    [StartOfLine] [LeadingSpace]   Loc=<main.m:28:9>
identifier 'eight'   [LeadingSpace] Loc=<main.m:28:13>
equal '='    [LeadingSpace] Loc=<main.m:28:19>
numeric_constant '8'     [LeadingSpace] Loc=<main.m:28:21 <Spelling=main.m:22:21>>
semi ';'        Loc=<main.m:28:32>
int 'int'    [StartOfLine] [LeadingSpace]   Loc=<main.m:29:9>
identifier 'six'     [LeadingSpace] Loc=<main.m:29:13>
equal '='    [LeadingSpace] Loc=<main.m:29:17>
numeric_constant '6'     [LeadingSpace] Loc=<main.m:29:19>
semi ';'        Loc=<main.m:29:20>
identifier 'NSString'    [StartOfLine] [LeadingSpace]   Loc=<main.m:30:9>
star '*'        Loc=<main.m:30:17>
identifier 'site'    [LeadingSpace] Loc=<main.m:30:19>
equal '='    [LeadingSpace] Loc=<main.m:30:24>
l_square '['     [LeadingSpace] Loc=<main.m:30:26>
l_square '['        Loc=<main.m:30:27>
identifier 'NSString'       Loc=<main.m:30:28>
identifier 'alloc'   [LeadingSpace] Loc=<main.m:30:37>
r_square ']'        Loc=<main.m:30:42>
identifier 'initWithUTF8String'  [LeadingSpace] Loc=<main.m:30:44>
colon ':'       Loc=<main.m:30:62>
string_literal '"starming"'     Loc=<main.m:30:63>
r_square ']'        Loc=<main.m:30:73>
semi ';'        Loc=<main.m:30:74>
int 'int'    [StartOfLine] [LeadingSpace]   Loc=<main.m:31:9>
identifier 'rank'    [LeadingSpace] Loc=<main.m:31:13>
equal '='    [LeadingSpace] Loc=<main.m:31:18>
identifier 'eight'   [LeadingSpace] Loc=<main.m:31:20>
plus '+'     [LeadingSpace] Loc=<main.m:31:26>
identifier 'six'     [LeadingSpace] Loc=<main.m:31:28>
semi ';'        Loc=<main.m:31:31>
identifier 'NSLog'   [StartOfLine] [LeadingSpace]   Loc=<main.m:32:9>
l_paren '('     Loc=<main.m:32:14>
at '@'      Loc=<main.m:32:15>
string_literal '"%@ rank %d"'       Loc=<main.m:32:16>
comma ','       Loc=<main.m:32:28>
identifier 'site'    [LeadingSpace] Loc=<main.m:32:30>
comma ','       Loc=<main.m:32:34>
identifier 'rank'    [LeadingSpace] Loc=<main.m:32:36>
r_paren ')'     Loc=<main.m:32:40>
semi ';'        Loc=<main.m:32:41>
r_brace '}'  [StartOfLine] [LeadingSpace]   Loc=<main.m:33:5>
return 'return'  [StartOfLine] [LeadingSpace]   Loc=<main.m:34:5>
numeric_constant '0'     [LeadingSpace] Loc=<main.m:34:12>
semi ';'        Loc=<main.m:34:13>
r_brace '}'  [StartOfLine]  Loc=<main.m:35:1>
eof ''      Loc=<main.m:35:2>

可以看出詞法分析只需要處理main.m中的代碼,把所有的字符串,符號(hào),括號(hào)都拆分開,拆出來的每一個(gè)部分,叫做token.

在接下來是語法分析
執(zhí)行:

clang -fmodules -fsyntax-only -Xclang -ast-dump main.m

輸出

-FunctionDecl 0x15ab12390 <line:26:1, line:35:1> line:26:5 main 'int ()'
  `-CompoundStmt 0x15b026d48 <col:11, line:35:1>
    |-ObjCAutoreleasePoolStmt 0x15b026d00 <line:27:5, line:33:5>
    | `-CompoundStmt 0x15b026cc8 <line:27:22, line:33:5>
    |   |-DeclStmt 0x15ab12530 <line:28:9, col:32>
    |   | `-VarDecl 0x15ab124a8 <col:9, line:22:21> line:28:13 used eight 'int' cinit
    |   |   `-IntegerLiteral 0x15ab12510 <line:22:21> 'int' 8
    |   |-DeclStmt 0x15ab4e6f8 <line:29:9, col:20>
    |   | `-VarDecl 0x15ab12560 <col:9, col:19> col:13 used six 'int' cinit
    |   |   `-IntegerLiteral 0x15ab125c8 <col:19> 'int' 6
    |   |-DeclStmt 0x15b024be8 <line:30:9, col:74>
    |   | `-VarDecl 0x15ab4e750 <col:9, col:73> col:19 used site 'NSString *' cinit
    |   |   `-ObjCMessageExpr 0x15ab50b90 <col:26, col:73> 'NSString * _Nullable':'NSString *' selector=initWithUTF8String:
    |   |     |-ObjCMessageExpr 0x15ab4eb58 <col:27, col:42> 'NSString *' selector=alloc class='NSString'
    |   |     `-ImplicitCastExpr 0x15ab50b78 <col:63> 'const char * _Nonnull':'const char *' <NoOp>
    |   |       `-ImplicitCastExpr 0x15ab50b60 <col:63> 'char *' <ArrayToPointerDecay>
    |   |         `-StringLiteral 0x15ab4ebc8 <col:63> 'char [9]' lvalue "starming"
    |   |-DeclStmt 0x15b0252a8 <line:31:9, col:31>
    |   | `-VarDecl 0x15b024c18 <col:9, col:28> col:13 used rank 'int' cinit
    |   |   `-BinaryOperator 0x15b024d20 <col:20, col:28> 'int' '+'
    |   |     |-ImplicitCastExpr 0x15b024cf0 <col:20> 'int' <LValueToRValue>
    |   |     | `-DeclRefExpr 0x15b024c80 <col:20> 'int' lvalue Var 0x15ab124a8 'eight' 'int'
    |   |     `-ImplicitCastExpr 0x15b024d08 <col:28> 'int' <LValueToRValue>
    |   |       `-DeclRefExpr 0x15b024cb8 <col:28> 'int' lvalue Var 0x15ab12560 'six' 'int'
    |   `-CallExpr 0x15b026c48 <line:32:9, col:40> 'void'
    |     |-ImplicitCastExpr 0x15b026c30 <col:9> 'void (*)(id, ...)' <FunctionToPointerDecay>
    |     | `-DeclRefExpr 0x15b0252c0 <col:9> 'void (id, ...)' Function 0x15b024d48 'NSLog' 'void (id, ...)'
    |     |-ImplicitCastExpr 0x15b026c80 <col:15, col:16> 'id':'id' <BitCast>
    |     | `-ObjCStringLiteral 0x15b025340 <col:15, col:16> 'NSString *'
    |     |   `-StringLiteral 0x15b025318 <col:16> 'char [11]' lvalue "%@ rank %d"
    |     |-ImplicitCastExpr 0x15b026c98 <col:30> 'NSString *' <LValueToRValue>
    |     | `-DeclRefExpr 0x15b025360 <col:30> 'NSString *' lvalue Var 0x15ab4e750 'site' 'NSString *'
    |     `-ImplicitCastExpr 0x15b026cb0 <col:36> 'int' <LValueToRValue>
    |       `-DeclRefExpr 0x15b025398 <col:36> 'int' lvalue Var 0x15b024c18 'rank' 'int'
    `-ReturnStmt 0x15b026d38 <line:34:5, col:12>
      `-IntegerLiteral 0x15b026d18 <col:12> 'int' 0

這一步會(huì)檢查語法的正確性,給出警告,報(bào)錯(cuò),以及修改建議.生成的內(nèi)容叫做抽象語法樹AST.

生成AST之后就可以開始生成IR代碼了
執(zhí)行:

clang -O3 -S -fobjc-arc -emit-llvm main.m -o main.ll

輸出main.ll文件,路徑和main.m相同,是可讀的.
這里"-O3"是LLVM的優(yōu)化策略,有-O1,-O3座云,-Os,也可以不設(shè)置.
如果設(shè)置了bitcode,還可以進(jìn)一步優(yōu)化.

接下來生成匯編

clang -S -fobjc-arc main.m -o main.s

生成目標(biāo)文件

clang -fmodules -c main.m -o main.o

生成可執(zhí)行文件并執(zhí)行

clang main.o -o main
./main

輸出

starming rank 14

2.從Xcode觀察編譯過程

這里的過程可以在buildsetting,Build Phases 和 Build Rules中進(jìn)行配置


查看

首先是預(yù)編譯,可以看到new build system等內(nèi)容.


預(yù)編譯

然后是編譯cocoapods的targets,包括創(chuàng)建framework(cocoapods使用use framework),copy頭文件,以及編譯.m文件


targets

接下來是主target,其實(shí)是與cocoapods的targes一致的,這一步主target也會(huì)被打包成framework.
也是拷貝.h文件,編譯swift文件,編譯.m文件,編譯xib文件,拷貝資源文件,


主target

接下來執(zhí)行cocoapods腳本,Build Phases的腳本.


腳本

最后拷貝swift標(biāo)準(zhǔn)庫以及簽名.


image.png

每一個(gè)都可以點(diǎn)開詳情.
比如編譯.m文件可以看到clang信息,這些基本是可以在build setting中進(jìn)行配置的.
前面是CompileC任務(wù)描述
然后是切換路徑
最后clang -x objective-c -target x86_64-apple-ios12.0-simulator ...就是編譯的命令


編譯一個(gè).m文件
  • 編譯的流程
    1.處理文件信息
    2.執(zhí)行CocoaPod編譯前腳本
    3.編譯.m文件(h文件是不需要編譯的),執(zhí)行clang命令
    4.鏈接framework
    5.拷貝和編譯xib,bundle文件
    6.編譯 ImageAssets
    7.處理 info.plist
    8.執(zhí)行CocoaPod腳本
    9.拷貝Swift標(biāo)準(zhǔn)庫
    10.創(chuàng)建.app文件和簽名

3.配置編譯選項(xiàng)

  • Build settings設(shè)置在build的過程中各個(gè)階段的選項(xiàng),clang的配置就屬于這個(gè)范圍.

  • Build Phases構(gòu)建可執(zhí)行文件的規(guī)則,指定 target 的依賴項(xiàng)目,指定在target build之前需要先build的依賴.
    在Compile Source中指定必須編譯的文件,這些文件同樣會(huì)根據(jù)Build Setting和Build Rules里的設(shè)置來處理.
    在Link Binary With Libraries里會(huì)列出所有的靜態(tài)庫和動(dòng)態(tài)庫,它們會(huì)和編譯生成的目標(biāo)文件鏈接.
    把靜態(tài)資源拷貝到bundle里.
    另外還可以通過在build phases里添加自定義腳本來做些事情,比如像CocoaPods所做的那樣.

  • Build Rules指定不同文件類型如何編譯,每條build rule指定了該類型如何處理以及輸出在哪,可以增加新規(guī)則對(duì)特定文件類型添加處理方法.

上面這些都是在Xcode UI中可視化的,這些信息最終需要以文件的格式保存下來,那就是.pbxproj文件,
路徑在[項(xiàng)目名稱].xcodeproj包里的project.pbxproj.

打開這個(gè)文件,在最后一行有一個(gè)

rootObject = F7036F002511EC050031CE83 /* Project object */;

搜索這個(gè)rootObject ID,可以找到PBXProject section,
這個(gè)文件就是以section為單位描述配置.

/* Begin PBXProject section */
        F7036F002511EC050031CE83 /* Project object */ = {
            isa = PBXProject;
            attributes = {
                CLASSPREFIX = XX;
                LastUpgradeCheck = 1310;
                ORGANIZATIONNAME = XXXX;
                TargetAttributes = {
                    F7036F072511EC050031CE83 = {
                        CreatedOnToolsVersion = 11.6;
                        LastSwiftMigration = 1240;
                    };
                };
            };
    ...

這PBXProject section里找到target

targets = (
            F7036F072511EC050031CE83 /* XXXXX */,
        );

再搜索這個(gè)ID,就可以找到更多配置,這個(gè).pbxproj文件就是以這種id索引的方式進(jìn)行記錄和查找.
比如繼續(xù)順著這個(gè)ID,可以找到更多的定義,可以看到buildPhases,buildConfiguration.
再順著找可以看到cocoapods, copy resource等等定義.

/* Begin PBXNativeTarget section */
        F7036F072511EC050031CE83 /* XXXXX */ = {
            isa = PBXNativeTarget;
            buildConfigurationList = F7036F212511EC080031CE83 /* Build configuration list for PBXNativeTarget "XXXXX" */;
            buildPhases = (
                2E9C3A1138A1AED934114EBC /* [CP] Check Pods Manifest.lock */,
                F7036F042511EC050031CE83 /* Sources */,
                F7036F052511EC050031CE83 /* Frameworks */,
                F7036F062511EC050031CE83 /* Resources */,
                34DB7EF940E3C9401AD2798F /* [CP] Embed Pods Frameworks */,
                25DB3E569FEFB4DC91CF364D /* [CP] Copy Pods Resources */,
            );
  ...
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疙赠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子朦拖,更是在濱河造成了極大的恐慌圃阳,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件璧帝,死亡現(xiàn)場(chǎng)離奇詭異捍岳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)睬隶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門锣夹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人苏潜,你說我怎么就攤上這事银萍。” “怎么了窖贤?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵砖顷,是天一觀的道長(zhǎng)贰锁。 經(jīng)常有香客問我赃梧,道長(zhǎng),這世上最難降的妖魔是什么豌熄? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任授嘀,我火速辦了婚禮,結(jié)果婚禮上锣险,老公的妹妹穿的比我還像新娘蹄皱。我一直安慰自己,他們只是感情好芯肤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布巷折。 她就那樣靜靜地躺著,像睡著了一般崖咨。 火紅的嫁衣襯著肌膚如雪锻拘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音署拟,去河邊找鬼婉宰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛推穷,可吹牛的內(nèi)容都是我干的心包。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼馒铃,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蟹腾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起骗露,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤岭佳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后萧锉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體珊随,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年柿隙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叶洞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡禀崖,死狀恐怖衩辟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情波附,我是刑警寧澤艺晴,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站掸屡,受9級(jí)特大地震影響封寞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仅财,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一狈究、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盏求,春花似錦抖锥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荆烈,卻和暖如春拯勉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工谜喊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留潭兽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓斗遏,卻偏偏與公主長(zhǎng)得像山卦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诵次,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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