Swift編譯流程 & Swift類

前言

本篇文章會大致分析一下swift的編譯流程,這個(gè)作為了解即可贝乎,然后會重點(diǎn)分析一下swift中類的結(jié)構(gòu)柴淘,這個(gè)知識點(diǎn)我們需要掌握

一示括、swift編譯流程

1.1 LLVM

在了解swift編譯流程之前铺浇,我們最好清楚LLVM是什么?請參考??LLVM編譯流程

LLVM是架構(gòu)編譯器的框架系統(tǒng)例诀,以C++編寫而成随抠,用于優(yōu)化任意程序語言編寫的程序的編譯時(shí)間(compile-time)鏈接時(shí)間(link-time)繁涂、運(yùn)行時(shí)間(run-time)以及空閑時(shí)間(idle-time)拱她。對開發(fā)者保持開放,并兼容已有腳本扔罪。

對于我們iOS系統(tǒng)秉沼,OC語言前端使用Clang編譯器,而swift語言前端使用swift編譯器矿酵,這兩個(gè)編譯器將我們寫的代碼編譯生成IR中間代碼唬复,交給LLVM優(yōu)化器進(jìn)行優(yōu)化,接著交給代碼生成器生成機(jī)器語言全肮,最終形成.o機(jī)器執(zhí)行文件敞咧。整個(gè)過程如下圖??

1.2 swift的編譯詳細(xì)流程

接著我們再詳細(xì)看看swift的編譯流程,如下圖??


大致分為以下幾步??

  1. swift源碼經(jīng)過parse解析辜腺、ast編譯休建,生成AST語法樹 乍恐。相關(guān)指令??

swiftc -dump-ast LGPerson.swift >> ast.swift

  1. 通過SIL生成器生成SIL源碼。相關(guān)指令??

swiftc -emit-sil LGPerson.swift >> ./LGPerson.sil

  1. 生成IR中間代碼测砂。相關(guān)指令??

swiftc -emit-ir LGPerson.swift >> ir.swift

  1. 輸出.o機(jī)器文件茵烈。相關(guān)指令??

swiftc -emit-object LGPerson.swift

1.3 示例查看編譯流程

在示例查看之前,我們可以在【終端】中swiftc -h查看swiftc的所有相關(guān)指令??

常用的一些指令的含義??

  -dump-ast             語法和類型檢查砌些,打印AST語法樹
  -dump-parse           語法檢查呜投,打印AST語法樹
  -dump-pcm             轉(zhuǎn)儲有關(guān)預(yù)編譯Clang模塊的調(diào)試信息
  -dump-scope-maps <expanded-or-list-of-line:column>
                         Parse and type-check input file(s) and dump the scope map(s)
  -dump-type-info        Output YAML dump of fixed-size types from all imported modules
  -dump-type-refinement-contexts
                         Type-check input file(s) and dump type refinement contexts(s)
  -emit-assembly         Emit assembly file(s) (-S)
  -emit-bc               輸出一個(gè)LLVM的BC文件
  -emit-executable       輸出一個(gè)可執(zhí)行文件
  -emit-imported-modules 展示導(dǎo)入的模塊列表
  -emit-ir               展示IR中間代碼
  -emit-library          輸出一個(gè)dylib動態(tài)庫
  -emit-object           輸出一個(gè).o機(jī)器文件
  -emit-pcm              Emit a precompiled Clang module from a module map
  -emit-sibgen           輸出一個(gè).sib的原始SIL文件
  -emit-sib              輸出一個(gè).sib的標(biāo)準(zhǔn)SIL文件
  -emit-silgen           展示原始SIL文件
  -emit-sil              展示標(biāo)準(zhǔn)的SIL文件
  -index-file            為源文件生成索引數(shù)據(jù)
  -parse                 解析文件
  -print-ast             解析文件并打印(漂亮/簡潔的)語法樹
  -resolve-imports       解析import導(dǎo)入的文件
  -typecheck             檢查文件類型

首先存璃,我們創(chuàng)建一個(gè)swift demo項(xiàng)目仑荐,定義一個(gè)類LGPerson.swift類

class LGPerson {
    var age: Int = 18
    var name: String = "luoji"
}

let t = LGPerson()

接著,打開【終端】有巧,進(jìn)入到項(xiàng)目的目錄

  • 查看抽象語法樹:swiftc -dump-ast LGPerson.swift ??
  • 生成SIL文件:swiftc -emit-sil LGPerson.swift >> ./LGPerson.sil


可以使用VSCode打開它看看??代碼量巨多

附:可以在.zshrc中做了如下配置释漆,這樣就能在終端中指定軟件打開相應(yīng)文件

// 打開.zshrc,如果提示打不開篮迎,則使用touch .zshrc 創(chuàng)建一個(gè)
$ open .zshrc

// 添加以下別名
alias vscode='/Applications/Visual\ Studio\ Code.app/Contents/Resources/app/bin/code'

// 如果之前是創(chuàng)建的男图,那么還需執(zhí)行
source .zshrc

//使用
$ swiftc -emit-sil LGPerson.swift >> ./LGPerson.sil && vscode LGPerson.sil

  • 如果想SIL文件高亮,需要安裝插件:VSCode SIL??

再次打開LGPerson.sil?? 沒有那么全白了

觀察LGPerson.sil代碼甜橱,可以發(fā)現(xiàn)很多都是經(jīng)過混淆處理的逊笆,可以通過下面的指令反混淆

xcrun swift-demangle 你的混淆的代碼

1.4 SIL分析

SIL: Swift intermediate language --> swift中間語言

1.4.1 main函數(shù)入口

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @$s8LGPerson1tA2ACvp               // id: %2
  %3 = global_addr @$s8LGPerson1tA2ACvp : $*LGPerson // user: %7
  %4 = metatype $@thick LGPerson.Type             // user: %6
  // function_ref LGPerson.__allocating_init()
  %5 = function_ref @$s8LGPersonAACABycfC : $@convention(method) (@thick LGPerson.Type) -> @owned LGPerson // user: %6
  %6 = apply %5(%4) : $@convention(method) (@thick LGPerson.Type) -> @owned LGPerson // user: %7
  store %6 to %3 : $*LGPerson                     // id: %7
  %8 = integer_literal $Builtin.Int32, 0          // user: %9
  %9 = struct $Int32 (%8 : $Builtin.Int32)        // user: %10
  return %9 : $Int32                              // id: %10
} // end sil function 'main'
  1. @main標(biāo)識當(dāng)前LGPerson.swift文件的入口函數(shù),SIL標(biāo)識符號名稱以@作為前綴岂傲。
  2. %0,%1...在SIL中也叫寄存器难裆,類似代碼中的常量,一旦賦值后不可修改镊掖。如果SIL中還要繼續(xù)使用乃戈,就需要使用新的寄存器。

我們一句句的看

  • alloc_global @$s8LGPerson1tA2ACvp
    s8LGPerson1tA2ACvp反混淆出來就是LGPerson亩进,那么就是創(chuàng)建全局變量LGPerson
  • %3 = global_addr @$s8LGPerson1tA2ACvp : $*LGPerson 這句很簡單症虑,讀取全局變量LGPerson地址,賦值給%3
  • %4 = metatype $@thick LGPerson.Type讀取LGPerson的Type归薛,賦值給%4
  • %5 = function_ref @$s8LGPersonAACABycfC : $@convention(method) (@thick LGPerson.Type) -> @owned LGPerson 定義一個(gè)function_ref即函數(shù)谍憔,就是%5,這個(gè)函數(shù)入?yún)⑹荓GPerson.Type
  • %6 = apply %5(%4) : $@convention(method) (@thick LGPerson.Type) -> @owned LGPerson apply調(diào)用函數(shù)%5主籍,入?yún)⒕褪?4习贫,將返回結(jié)果賦給%6
  • store %6 to %3 : $*LGPerson 將%6的結(jié)果存儲到%3,%3是LGPerson的地址
  • %8 = integer_literal $Builtin.Int32, 0%9 = struct $Int32 (%8 : $Builtin.Int32)就是構(gòu)建一個(gè)Int值千元,最終返回return %9 : $Int32

綜上所述苫昌,main函數(shù)就是構(gòu)建了一個(gè)全局變量LGPerson,并對其Type值做了一個(gè)返回處理幸海,那么蜡歹,這個(gè)Type代表什么意思呢屋厘?后面我們會仔細(xì)分析swift類的底層結(jié)構(gòu)。

1.4.2 實(shí)例化相關(guān)代碼

// LGPerson.__allocating_init()
sil hidden [exact_self_class] @$s8LGPersonAACABycfC : $@convention(method) (@thick LGPerson.Type) -> @owned LGPerson {
// %0 "$metatype"
bb0(%0 : $@thick LGPerson.Type):
  %1 = alloc_ref $LGPerson                        // user: %3
  // function_ref LGPerson.init()
  %2 = function_ref @$s8LGPersonAACABycfc : $@convention(method) (@owned LGPerson) -> @owned LGPerson // user: %3
  %3 = apply %2(%1) : $@convention(method) (@owned LGPerson) -> @owned LGPerson // user: %4
  return %3 : $LGPerson                           // id: %4
} // end sil function '$s8LGPersonAACABycfC'

同樣一句句的看

  • %1 = alloc_ref $LGPerson 讀取LGPerson的alloc_ref方法地址月而,給%1
  • %2 = function_ref @$s8LGPersonAACABycfc : $@convention(method) (@owned LGPerson) -> @owned LGPerson 讀取LGPerson.init()函數(shù)地址,給%2
  • %3 = apply %2(%1) : $@convention(method) (@owned LGPerson) -> @owned LGPerson 調(diào)用alloc_ref創(chuàng)建一個(gè)LGPerson實(shí)例對象议纯,給%3
  • return %3 : $LGPerson返回%3的實(shí)例對象

這個(gè)__allocating_init()也不難父款,其實(shí)就是一個(gè)簡單的方法調(diào)用過程。對于這個(gè)SIL代碼瞻凤,我們平時(shí)可以自己多看多分析憨攒,習(xí)慣就好了,哈哈阀参!

二肝集、swift類的結(jié)構(gòu)

帶著上面的問題??LGPerson中的Type 就是存儲的是什么值,代表什么意思蛛壳?我們現(xiàn)在來看看swift的類class在底層中的結(jié)構(gòu)是什么樣的杏瞻。

大家都知道,如果要創(chuàng)建一個(gè)對象衙荐,OC 與 swift的寫法是這樣的??

  • OC: [[LGPerosn alloc] init]
    一般alloc申請內(nèi)存空間并創(chuàng)建對象捞挥,init對其進(jìn)行統(tǒng)一初始化處理。
  • Swift:LGPerson()
    直接()就完成了對象的創(chuàng)建忧吟。

2.1 找入口

我們現(xiàn)在就來看看LGPerson()在匯編層是調(diào)用了哪些函數(shù)砌函?
新建一個(gè)SwiftDemo項(xiàng)目工程,在ViewController.swift中添加下面的代碼溜族,并打上斷點(diǎn)??

運(yùn)行項(xiàng)目讹俊,查看匯編??

我們找到了對象的初始化入口??函數(shù)__allocating_init(),接著我們在__allocating_init()這一行打上斷點(diǎn)煌抒,按住control按鍵 stepinto進(jìn)入查看??

定位到了swift_allocObject仍劈,同理還是在swift_allocObject這行加上斷點(diǎn),stepinto進(jìn)入查看??

沒找到實(shí)用的信息摧玫,那么換種思路耳奕,添加符號斷點(diǎn)swift_allocObject,再次run??

接著诬像,添加符號斷點(diǎn)swift_slowAlloc??

最終來到了比較熟悉的malloc_zone_malloc屋群。

至此,swift實(shí)例對象的創(chuàng)建流程??
__allocating_init -> swift_allocObject -> _swift_allocObject_ -> swift_slowAlloc -> malloc_zone_malloc

2.2 查看源碼驗(yàn)證流程

首先坏挠,用VSCode打開swift源碼項(xiàng)目芍躏,搜索__allocating_init,打上斷點(diǎn)??

接著降狠,run項(xiàng)目对竣,在終端輸入代碼??

class LGPerson {
       var age: Int = 18
       var name: String = "luoji"
}

回車庇楞,再輸入var t = LGPerson(),會觸發(fā)斷點(diǎn)??

我們可以看左上角否纬,發(fā)現(xiàn)requiredSize空間大小值是40吕晌,requiredAlignmentMask字節(jié)對齊值是7。

接著我們順著流程來到swift_slowAlloc??

然后返回申請空間后的地址临燃,即返回指針p睛驳。再回到_swift_allocObject_,那么接著執(zhí)行

  • auto object = reinterpret_cast<HeapObject *>( swift_slowAlloc(requiredSize, requiredAlignmentMask));
    通過reinterpret_cast將指針p轉(zhuǎn)換成HeapObject類型膜廊,然后執(zhí)行
  • new (object) HeapObject(metadata);
    因?yàn)閛bject是強(qiáng)轉(zhuǎn)的HeapObject類型乏沸,其里面值仍是一個(gè)指向內(nèi)存空間的對象指針,所以這里執(zhí)行HeapObject(metadata)做一個(gè)初始化操作爪瓜,這樣object就是真正的HeapObject結(jié)構(gòu)了蹬跃。

整體流程圖??

2.2.1 類的大小

接下來我們具體看看,類的大小size是怎么算出來的铆铆?
首先蝶缀,我們先在lldb中打印查看一下大小,打開Xcode算灸,新建一個(gè)swift項(xiàng)目扼劈,加入代碼

import UIKit

class LGPerson {
    var age: Int = 18
    var name: String = "luoji"
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
                
        print("Int32 大小: \(MemoryLayout<Int32>.size)")
        print("Int64 大小: \(MemoryLayout<Int64>.size)")
        print("Int 大小: \(MemoryLayout<Int>.size)")
        print("Srtring 大小: \(MemoryLayout<String>.size)")
        
        print("LGPerson 大小: \(class_getInstanceSize(LGPerson.self))")
        
    }
}

run??

我們通過MemoryLayout得知Int大小是8String大小是16菲驴,但是通過class_getInstanceSize得知類LGPerson的大小卻是40,40-16-8=16,這額外的16字節(jié)大小是什么荐吵?

之前我們在源碼調(diào)試的時(shí)候,也發(fā)現(xiàn)類LGPerson初始化實(shí)例對象時(shí)的requiredSize空間大小值也是40赊瞬,如下圖??

額外的16字節(jié)從何而來先煎?

我們進(jìn)入源碼工程,查看HeapObject結(jié)構(gòu)體??

結(jié)構(gòu)體大小主要看成員變量巧涧,HeapObject結(jié)構(gòu)體有兩個(gè)成員變量元數(shù)據(jù)metadata引用計(jì)數(shù)refCounts薯蝎。

metadata

是 HeapMetedata類型,還是查看源碼??

template <typename Target> struct TargetHeapMetadata;
using HeapMetadata = TargetHeapMetadata<InProcess>;

template <typename Runtime>
struct TargetHeapMetadata : TargetMetadata<Runtime> {
  using HeaderType = TargetHeapMetadataHeader<Runtime>;

  TargetHeapMetadata() = default;
  constexpr TargetHeapMetadata(MetadataKind kind)
    : TargetMetadata<Runtime>(kind) {}
#if SWIFT_OBJC_INTEROP
  constexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa)
    : TargetMetadata<Runtime>(isa) {}
#endif
};

TargetHeapMetadata->TargetMetadata谤绳,也是struct結(jié)構(gòu)體占锯。接著看TargetMetadata??

最后我們看看StoredPointer源碼,發(fā)現(xiàn)搜不到缩筛,但是根據(jù)注釋

  /// The kind. Only valid for non-class metadata; getKind() must be used to get
  /// the kind value.
  StoredPointer Kind;

我們發(fā)現(xiàn), 可以通過 getKind()方法入?yún)⒅??

  /// Get the metadata kind.
  MetadataKind getKind() const {
    return getEnumeratedMetadataKind(Kind);
  }

/// Try to translate the 'isa' value of a type/heap metadata into a value
/// of the MetadataKind enum.
inline MetadataKind getEnumeratedMetadataKind(uint64_t kind) {
  if (kind > LastEnumeratedMetadataKind)
    return MetadataKind::Class;
  return MetadataKind(kind);
}

getKind是調(diào)用getEnumeratedMetadataKind消略,而入?yún)?code>kind是uint64_t類型,占8字節(jié)大小瞎抛,所以元數(shù)據(jù)metadata8字節(jié)大小艺演。

MetadataKind

這時(shí)我們注意到,getKind()返回值是MetadataKind類型,我們進(jìn)入源碼看看胎撤,里面有一個(gè)#include "MetadataKind.def"晓殊,點(diǎn)擊進(jìn)入,其中記錄了所有類型的元數(shù)據(jù)伤提,所以kind種類總結(jié)如下:

name value
Class 0x0
Struct 0x200
Enum 0x201
Optional 0x202
ForeignClass 0x203
Opaque 0x300
Tuple 0x301
Function 0x302
Existential 0x303
Metatype 0x304
ObjCClassWrapper 0x305
ExistentialMetatype 0x306
HeapLocalVariable 0x400
HeapGenericLocalVariable 0x500
ErrorObject 0x501
LastEnumerated 0x7FF

接著我們回到TargetMetaData結(jié)構(gòu)體定義中巫俺,找方法getClassObject,在該方法中去匹配kind返回值是TargetClassMetadata類型??

如果是Class肿男,則直接對this(當(dāng)前指針识藤,即metadata)強(qiáng)轉(zhuǎn)ClassMetadata。所以次伶,TargetMetadataTargetClassMetadata 本質(zhì)上是一樣的,因?yàn)樵趦?nèi)存結(jié)構(gòu)中稽穆,可以直接進(jìn)行指針的轉(zhuǎn)換冠王,那么我們可以這么認(rèn)為??結(jié)構(gòu)體其實(shí)就是TargetClassMetadata

接著舌镶,我們再看看TargetClassMetadata里還有哪些成員??

template <typename Runtime>
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
    ...
    //swift特有的標(biāo)志
    ClassFlags Flags;
    //實(shí)力對象內(nèi)存大小
    uint32_t InstanceSize;
    //實(shí)例對象內(nèi)存對齊方式
    uint16_t InstanceAlignMask;
    //運(yùn)行時(shí)保留字段
    uint16_t Reserved;
    //類的內(nèi)存大小
    uint32_t ClassSize;
    //類的內(nèi)存首地址
    uint32_t ClassAddressPoint;
  ...
}

然后我們看看繼承鏈?? TargetClassMetadata繼承TargetAnyClassMetadata繼承TargetHeapMetadata柱彻。

template <typename Runtime>
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
    ...
    ConstTargetMetadataPointer<Runtime, swift::TargetClassMetadata> Superclass;
    TargetPointer<Runtime, void> CacheData[2];
    StoredSize Data;
    ...
}

至此,當(dāng)metadata的kind為Class時(shí)餐胀,有如下繼承鏈:

  • 類class在底層中的實(shí)際類型是TargetClassMetadata哟楷,而TargetMetaData中只有一個(gè)屬性kindTargetAnyClassMetaData中有4個(gè)屬性否灾,分別是kind卖擅, superclass,cacheData墨技、data(圖中未標(biāo)出)
  • 當(dāng)前Class在內(nèi)存中所存放的屬性由 TargetClassMetadata屬性 + TargetAnyClassMetaData屬性 + TargetMetaData屬性 構(gòu)成惩阶,所以得出的metadata的數(shù)據(jù)結(jié)構(gòu)體如下所示
struct swift_class_t: NSObject{
    void *kind;//相當(dāng)于OC中的isa,kind的實(shí)際類型是unsigned long
    void *superClass;
    void *cacheData;
    void *data;
    uint32_t flags; //4字節(jié)
    uint32_t instanceAddressOffset;//4字節(jié)
    uint32_t instanceSize;//4字節(jié)
    uint16_t instanceAlignMask;//2字節(jié)
    uint16_t reserved;//2字節(jié)
    
    uint32_t classSize;//4字節(jié)
    uint32_t classAddressOffset;//4字節(jié)
    void *description;
    ...
}

refCounts

接著我們看看refCounts扣汪,refCountsInlineRefCounts類型断楷,搜索InlineRefCounts??

typedef RefCounts<InlineRefCountBits> InlineRefCounts;

InlineRefCounts是RefCounts類型,搜索??

RefCounts是class類型崭别,確切的說冬筒,refCounts是個(gè)指針,占8字節(jié)大小茅主。

綜上所述

  1. swift類本質(zhì)是HeapObject
  2. HeapObject默認(rèn)大小為16字節(jié): metadata(struct)8字節(jié)和refCounts(class)8字節(jié)
  3. LGPerson的age(Int)占8字節(jié)舞痰,name(String)占16字節(jié),加上上面的暗膜,所以LGPersonsize為40字節(jié)

總結(jié)

本篇文章首先大致講述了Swift的編譯流程匀奏,與OC最大的不同在于,swift在編譯過程中會生成SIL中間代碼学搜,通過對中間代碼的分析娃善,我明知道了Swift實(shí)例對象的初始化會調(diào)用__allocating_init()方法论衍,接著我們通過LGPerson樣例分析了Swift類的底層結(jié)構(gòu)HeapObject,其默認(rèn)包含了metadata(struct)refCounts(class)聚磺,共16字節(jié)大小坯台。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瘫寝,隨后出現(xiàn)的幾起案子蜒蕾,更是在濱河造成了極大的恐慌,老刑警劉巖焕阿,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咪啡,死亡現(xiàn)場離奇詭異,居然都是意外死亡暮屡,警方通過查閱死者的電腦和手機(jī)撤摸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來褒纲,“玉大人准夷,你說我怎么就攤上這事≥郝樱” “怎么了衫嵌?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長彻秆。 經(jīng)常有香客問我楔绞,道長,這世上最難降的妖魔是什么掖棉? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任墓律,我火速辦了婚禮,結(jié)果婚禮上幔亥,老公的妹妹穿的比我還像新娘耻讽。我一直安慰自己,他們只是感情好帕棉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布针肥。 她就那樣靜靜地躺著,像睡著了一般香伴。 火紅的嫁衣襯著肌膚如雪慰枕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天即纲,我揣著相機(jī)與錄音具帮,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蜂厅,可吹牛的內(nèi)容都是我干的匪凡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼掘猿,長吁一口氣:“原來是場噩夢啊……” “哼病游!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起稠通,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤衬衬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后改橘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滋尉,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年飞主,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了兼砖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡既棺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出懒叛,到底是詐尸還是另有隱情丸冕,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布薛窥,位于F島的核電站胖烛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏诅迷。R本人自食惡果不足惜佩番,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罢杉。 院中可真熱鬧趟畏,春花似錦、人聲如沸滩租。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽律想。三九已至猎莲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間技即,已是汗流浹背著洼。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人身笤。 一個(gè)月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓豹悬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親展鸡。 傳聞我的和親對象是個(gè)殘疾皇子屿衅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

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