Swift語言的類與結構體--1

一钝满、類與結構體的異同

  1. 相同點
    • 定義存儲值的屬性
    • 定義方法
    • 定義初始化器
    • 定義下標,并使用下表語法訪問其值
    • 使用extension來擴展功能
    • 遵循協(xié)議來提供某種功能
  2. 不同點
    • 類有繼承质况,而結構體沒有
    • 類型轉換使得您能夠在運行時檢查和解釋類實例的類型
    • 類有析構函數(shù)來釋放其分配的資源
    • 類有引用計數(shù)記錄對一個是咧的引用次數(shù)

類是引用類型肝谭。意味著仆救,一個類型的變量并不直接存儲具體的實例對象,而是存儲具體實例對象的內(nèi)存地址膳沽。\color{#88ff00}{內(nèi)存是存在堆上}

// 類的時候
class PSYModel{
    var age: Int
    var name: String
    
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
}

var t = PSYModel.init(age: 18, name: "psy")

var t1 = t

print("end")
引用類型

可以通過lldb命令:cat address 0x100570af0查看其實存取的區(qū)域汗菜,可以發(fā)現(xiàn)是在heap(堆)區(qū)。

實例地址內(nèi)存區(qū)域

結構體是值類型(值類型還有enum枚舉等)挑社。意味著陨界,一個結構體類型的存儲是具體的實例值,而不是像引用類型存儲的是具體實例對象的內(nèi)存地址痛阻。\color{#88ff00}{一般情況下存儲在棧上}

// 結構體的時候
struct PSYModel{
    var age: Int
    var name: String
    
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
}

var t = PSYModel.init(age: 18, name: "psy")

var t1 = t

t.name = "俏~"

print("end")
值類型輸出

由上面預演的可知菌瘪,打印tt1都是直接打印出結構體的值,并且阱当,t1t的一個副本俏扩,類似于本地化的一份,修改任何一個實例化的值對另一個實例沒有影響弊添。

image.png

結構體存儲

關于內(nèi)存可以先了解一下 內(nèi)存管理的五大區(qū) 這篇文章录淡,以下主要驗證相關的各個內(nèi)存區(qū)域:

棧區(qū)(stack):局部變量,參數(shù)油坝,函數(shù)運行時上下文

堆區(qū)(heap):存儲多有對象嫉戚,由程序員/系統(tǒng)申請并由程序員/系統(tǒng)釋放

全局區(qū)(global)

全局

常量區(qū)(data):

代碼區(qū)(text)

結構體類型中如果添加了引用類型的成員變量,則會在堆區(qū)申請空間澈圈,而原來的結構體實例存儲的位置不變彬檀,只是引用類型的成員變量區(qū)域存儲的是指向實例的堆區(qū)的內(nèi)存指針,如下圖:


結構體中添加引用類型成員

所以在結構體中盡量不要添加引用類型成員變量瞬女,因為那樣會涉及到堆內(nèi)存的申請和釋放窍帝,影響性能。

二诽偷、初始化器

1. 執(zhí)行初始化器&便捷初始化器

類編譯器默認不會自動提供成員初始化器坤学,也就是如果類中有兩個成員疯坤,如果沒有初始化,則會報錯拥峦。但是類中在沒有成員的時候贴膘,會默認有一個init(){}初始化器卖子。


結構體默認自動提供初始化器
結構體默認有成員初始化器

Swift中創(chuàng)建類的實例時必須為所有的存儲屬性設置初始值略号,因為swift會根據(jù)類型確定變量的類型或者根據(jù)初始值推斷變量類型,這就是類型安全洋闽。所以類必須提供指定初始化器玄柠,也可以根據(jù)需要提供便捷初始化器(在初始化前面加上convenience關鍵字,便捷初始化器必須從相同的類中調用指定初始化器)诫舅。

image.png

便捷初始化器定義有一些規(guī)則羽利,加上convenience關鍵字之后,需要嚴格控制初始化器的創(chuàng)建 規(guī)則:

  • 指定初始化器必須保證在向上委托給父類初始化器之前刊懈,其所在類引入的所有屬性都要初始化完成这弧。

  • 指定初始化器必須先向上委托父類初始化器,然后才能為繼承的屬性設置新值虚汛。如果不這樣做匾浪,指定初始化器賦予的新值將被父類中的初始化器所覆蓋

  • 便捷初始化器必須先委托同類中的其它初始化器,然后再為任意屬性賦新值(包括 同類里定義的屬性)卷哩。如果沒這么做蛋辈,便捷構初始化器賦予的新值將被自己類中其它指定初始化器所覆蓋。

  • 初始化器在第一階段初始化完成之前将谊,不能調用任何實例方法冷溶、不能讀取任何實例屬性的值,也不能引用 self 作為值尊浓。

2. 可失敗初始化器

可失敗初始化器 : 可以根據(jù)業(yè)務需要逞频,根據(jù)參數(shù)d的\color{#ff0000}{合法性}決定是否初始化失敗,也就是return nil

可失敗初始化器

三栋齿、類的生命周期

1. Swift的編譯流程

iOS開發(fā)語言OC和Swift后端都是通過LLVM進行編譯最終生成可執(zhí)行文件虏劲;

編譯器

OC的編譯是通過clang編譯器,生成LLVM中間IR代碼褒颈,最后由后端生成可執(zhí)行文件.o柒巫;
Swift是通過Swift編譯器編譯生成,sil文件谷丸,再生成LLVM的可操作性的IR中間代碼堡掏,然后再生成可執(zhí)行文件。在這個過程中刨疼,區(qū)別就是前段編譯器不一樣泉唁,多了一個步驟是生成了sil文件鹅龄,一下是編譯流程圖:


Swift編譯過程

其生成步驟可以單步在終端輸入特定的指令生成中間的代碼,指令集如下:

// 分析輸出AST
swiftc main.swift -dump-parse
// 分析并且檢查類型輸出AST
swiftc main.swift -dump-ast
// 生成中間體語言(SIL)亭畜,未優(yōu)化
swiftc main.swift -emit-silgen
// 生成中間體語言(SIL)扮休,優(yōu)化后的
swiftc main.swift -emit-sil
// 生成LLVM中間體語言 (.ll文件)
swiftc main.swift -emit-ir
// 生成LLVM中間體語言 (.bc文件)
swiftc main.swift -emit-bc
// 生成匯編
swiftc main.swift -emit-assembly
// 編譯生成可執(zhí)行.out文件
swiftc -o main.o main.swift


2. Swift特有的(區(qū)別于OC)SIL代碼

生成SIL文件的時候可以執(zhí)行輸出main.c文件,方便Xcode查看拴鸵,指令如下:swiftc main.swift -emit-sil -o main.c

SIL的基本語法:SIL官方基本語法

@main: 入口函數(shù)玷坠, @作為標識符
%0、%1劲藐、%2....: 可理解為虛擬的寄存器八堡,類似于日常開發(fā)中的常量,一旦賦值不可修改聘芜,最后如果跑到設備商會使用真的寄存器
% 局部標識
alloca 開辟空間
align 內(nèi)存對齊
i32 32位兄渺, 4字節(jié)
store 寫入內(nèi)存
load 讀取數(shù)據(jù)
call 調用函數(shù)
ret 返回
s4main1tAA8PSYModelCvp這種是混寫的,可以使用終端使用命令還原:xcrun swift-demangle s4main1tAA8PSYModelCvp
load: 讀取數(shù)據(jù)
sil_global:標記變量為全局變量
hidden: 標記只針對同一個Swift模塊中的對象可見
alloc_global: 開辟全局變量的內(nèi)存
global_addr: 獲取全局變量的地址
ref_element_addr: 獲取元素地址
init_existential_addr: 指令會生成 Existential Container 結構, 包裹著實例變量和協(xié)議對應的 PWT
destroy_addr
bb0 / bb1 ... : basic block 數(shù)字,表示一個代碼塊汰现,SIL中沒有分支語句挂谍,只有入口和出口
alloc_ref / dealloc_ref: 開辟/釋放內(nèi)存
function_ref: 獲取直接派發(fā)函數(shù)地址.
class_method: 通過函數(shù)表獲取方法.
witness_method: 通過 PWT 獲取對應的函數(shù)地址
objc_method : 獲取OC 方法地址
apply:調用函數(shù)
store A to B : 把A 的值存儲到B中。
begin_access / end_access: 開始瞎饲、結束訪問
[modify] / [read] / [deinit] :修改型訪問口叙、讀取型訪問、刪除型訪問
[dynamic]:動態(tài)訪問
[static]:靜態(tài)訪問
retain_value: 引用計數(shù) + 1
release_value: 引用計數(shù) - 1
metatype 獲得元類型
@thick 描述元類型代表的形式企软,是引用 對象類型或是其子類,
@thin 代表一個確切的值 類型庐扫,不需要存儲,
$ : 類型標識
%: 表示寄存器,類似局部常量仗哨,賦值后不可修改形庭。如果再需要新的寄存器,就增加寄存器編號厌漂,這樣操作有利于編譯器的優(yōu)化萨醒;后續(xù)進行降級操作 時,才會把這些帶編號的虛擬寄存器 轉換成對應體系結構的真實寄存器苇倡。
@ : SIL中所有標識符均以@符號開頭
@main 方法名字是 main
@_hasStorage 標識屬性是存儲屬性
@_hasInitialValue 標識屬性有初始值
@owned 代表函數(shù)接收者負責銷毀返回值
@convention 這個標識用于明確指定當函數(shù)調用時參數(shù)和返回值應該如何被處理
@convention(c) 表示使用C函數(shù)的方式進行調用
@convention(swift) 純Swift函數(shù)的默認調用方式
@convention(method) 柯里化的函數(shù)調用方式
@convention(witness_method) 協(xié)議方法調用其监,它等同于convention(method)滞乙,除了在處理范型類型參數(shù)時
@convention(objc_method) Objective-C方式調用

SIL代碼以及分析注釋如下:

sil_stage canonical

import Builtin
import Swift
import SwiftShims

class PSYModel {
  @_hasStorage var age: Int { get set }  // 有一個變量age届谈,還有set和get方法
  @_hasStorage var name: String { get set } // 有一個變量name魔吐,還有set和get方法
  init(age: Int, name: String) // 有一個指定初始化器
  @objc deinit  // 析構函數(shù),@objc標識符
}

// 一個已經(jīng)初始化的變量t
@_hasStorage @_hasInitialValue var t: PSYModel { get set } 
// 一個已經(jīng)初始化的變量t1
@_hasStorage @_hasInitialValue var t1: PSYModel { get set }

// t
sil_global hidden @$s4main1tAA8PSYModelCvp : $PSYModel

// t1
sil_global hidden @$s4main2t1AA8PSYModelCvp : $PSYModel

// main 入口函數(shù)
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
// 分配一個全局變量t,     s4main1tAA8PSYModelCvp是混寫之后的
  alloc_global @$s4main1tAA8PSYModelCvp           // id: %2
// 拿到全局變量的地址--->%3
  %3 = global_addr @$s4main1tAA8PSYModelCvp : $*PSYModel // users: %15, %18
// 
  %4 = metatype $@thick PSYModel.Type             // user: %14
  %5 = integer_literal $Builtin.Int64, 18         // user: %6
  %6 = struct $Int (%5 : $Builtin.Int64)          // user: %14
  %7 = string_literal utf8 "psy"                  // user: %12
  %8 = integer_literal $Builtin.Word, 3           // user: %12
  %9 = integer_literal $Builtin.Int1, -1          // user: %12
  %10 = metatype $@thin String.Type               // user: %12
  // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
// 函數(shù)引用String.init综慎,有三個參數(shù)
  %11 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %12
// 調用String.init("psy", 3 , -1, String.Type)涣仿,得到一個字符串
  %12 = apply %11(%7, %8, %9, %10) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %14
  // function_ref PSYModel.__allocating_init(age:name:)
// 函數(shù)引用__allocating_init(age:name:)
  %13 = function_ref @$s4main8PSYModelC3age4nameACSi_SStcfC : $@convention(method) (Int, @owned String, @thick PSYModel.Type) -> @owned PSYModel // user: %14
// 實例化一個對象調用指定初始化器,得到PSYModel對象
  %14 = apply %13(%6, %12, %4) : $@convention(method) (Int, @owned String, @thick PSYModel.Type) -> @owned PSYModel // user: %15
// 將對象存儲到全局變量
  store %14 to %3 : $*PSYModel                    // id: %15
// 在一個全局變量 t1
  alloc_global @$s4main2t1AA8PSYModelCvp          // id: %16
// 拿到全局變量的內(nèi)存地址
  %17 = global_addr @$s4main2t1AA8PSYModelCvp : $*PSYModel // user: %19
// 開始:動態(tài)讀取t全局變量內(nèi)存
  %18 = begin_access [read] [dynamic] %3 : $*PSYModel // users: %20, %19
// 地址拷貝存到t1的內(nèi)存地址
  copy_addr %18 to [initialization] %17 : $*PSYModel // id: %19
// 結束:
  end_access %18 : $*PSYModel                     // id: %20

一下分析同理。好港。愉镰。。钧汹。丈探。。拔莱。碗降。

  %21 = integer_literal $Builtin.Word, 1          // user: %23
  // function_ref _allocateUninitializedArray<A>(_:)
  %22 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %23
  %23 = apply %22<Any>(%21) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %25, %24
  %24 = tuple_extract %23 : $(Array<Any>, Builtin.RawPointer), 0 // users: %43, %40
  %25 = tuple_extract %23 : $(Array<Any>, Builtin.RawPointer), 1 // user: %26
  %26 = pointer_to_address %25 : $Builtin.RawPointer to [strict] $*Any // user: %33
  %27 = string_literal utf8 "end"                 // user: %32
  %28 = integer_literal $Builtin.Word, 3          // user: %32
  %29 = integer_literal $Builtin.Int1, -1         // user: %32
  %30 = metatype $@thin String.Type               // user: %32
  // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
  %31 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %32
  %32 = apply %31(%27, %28, %29, %30) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %34
  %33 = init_existential_addr %26 : $*Any, $String // user: %34
  store %32 to %33 : $*String                     // id: %34
  // function_ref default argument 1 of print(_:separator:terminator:)
  %35 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA0_ : $@convention(thin) () -> @owned String // user: %36
  %36 = apply %35() : $@convention(thin) () -> @owned String // users: %42, %40
  // function_ref default argument 2 of print(_:separator:terminator:)
  %37 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA1_ : $@convention(thin) () -> @owned String // user: %38
  %38 = apply %37() : $@convention(thin) () -> @owned String // users: %41, %40
  // function_ref print(_:separator:terminator:)
  %39 = function_ref @$ss5print_9separator10terminatoryypd_S2StF : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> () // user: %40
  %40 = apply %39(%24, %36, %38) : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> ()
  release_value %38 : $String                     // id: %41
  release_value %36 : $String                     // id: %42
  release_value %24 : $Array<Any>                 // id: %43
  %44 = integer_literal $Builtin.Int32, 0         // user: %45
  %45 = struct $Int32 (%44 : $Builtin.Int32)      // user: %46
  return %45 : $Int32                             // id: %46
} // end sil function 'main'

3. 類的加載流程
3.1 純Swift代碼

??在菜單欄:Debug --> Debug workflow --> Always show Disassembly運行源碼,斷點可看到匯編代碼辨宠,由于筆者運行的是Mac上遗锣,所以只要關注call指令即可(ARM64關注bl货裹、b嗤形、br等指令)』≡玻看到下斷點到call __allocating_init的指令出,然后按住control + ?箭頭單步進入可看到如下圖:

純Swift

純Swift流程

swift_allocObject流程的時候赋兵,單步進入,里面并沒有看到流程了搔预,此時需要借助Swift源碼進行分析霹期。

??使用VS Code打開 Swift源碼 全局搜索swift_allocObject,或者直接command+p輸入HeapObject定位到文件拯田,找到swift_allocObject函數(shù)历造,在其上方有一個_swift_allocObject_函數(shù):

_swift_allocObject_函數(shù)

swift_slowAlloc函數(shù)

swift_slowAlloc
純swift流程
3.2繼承自NSObject
繼承自NSObject
4.對象與類的內(nèi)存結構

我們從上面的_swift_allocObject_中可以看到return的** object**對象是auto object = reinterpret_cast<HeapObject *>(swift_slowAlloc(requiredSize, requiredAlignmentMask))這條語句生成的,類型是HeapObject:
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \ InlineRefCounts refCounts

image.png

HeapMetadata只是一個別名船庇,真正是TargetHeapMetadata

image.png

TargetHeapMetadata又繼承自TargetMetadata吭产,這個里面根據(jù)MetadataKind創(chuàng)建TargetHeapMetadata,并且如果需要SwiftObject-C交互(SWIFT_OBJC_INTEROP默認為1)還根據(jù)TargetAnyClassMetadata也就是isa創(chuàng)建鸭轮。
image.png

TargetMetadata結構體臣淤,基本到這里,這就是基類了窃爷,但是還是沒有看見其成員變量邑蒋,基本到這就到了瓶頸了。

TargetMetadata

但是既然kind可以理解為isa按厘,那么基本MetadataKind就是一個重點医吊,在類型的descriptor下,什么情況下是類逮京,然后就發(fā)現(xiàn)了這個函數(shù)getTypeContextDescriptor,根據(jù)kind來區(qū)分是class還是其他卿堂,如果是類的時候注意到TargetClassMetadata

kind:
1.class
2.Struct
3.Enum
4.Optional
5.ForeignClass


TargetClassMetadata函數(shù),以及父類TargetAnyClassMetadata
TargetClassMetadata

TargetAnyClassMetadata

由此基本上類的數(shù)據(jù)結構可以展開為:

struct Metadata{
??var kind: Int
??var superClass: Any.Type
??var cacheData: (Int, Int)
??var data: Int
??var classFlags: Int32
??var instanceAddressPoint: UInt32
??var instanceSize: UInt32
??var instanceAlignmentMask: UInt16
??var reserved: UInt16
??var classSize: UInt32
??var classAddressPoint: UInt32
??var typeDescriptor: UnsafeMutableRawPointer
??var iVarDestroyer: UnsafeRawPointer
}

5.驗證--將結構體綁定成為指針類型
// 實例對象的數(shù)據(jù)結構
// UnsafeRawPointer 就是一個原生指針
// 有一個64位的refcounted造虏,可以拆分成兩個32位的
struct HeapObject{
    var metadata: UnsafeRawPointer
    var refcounted1: UInt32
    var refcounted2: UInt32
}

class PSYModel{
  var age: Int = 18
  var name: String = "psy"
}

var t = PSYModel()

// 獲取實例對象的指針是一個原生指針類型
let objcRawPtr = Unmanaged.passUnretained(t as AnyObject).toOpaque()
// 綁定內(nèi)存
let objcPtr = objcRawPtr.bindMemory(to: HeapObject.self
    , capacity: 1)

print("end")

下斷點lldb打佑獭:


// Metadata類似于類內(nèi)存結構
struct Metadata{
    var kind: Int   // 可理解成isa
    var superClass: Any.Type
    var cacheData: (Int, Int)
    var data: Int
    var classFlags: Int32
    var instanceAddressPoint: UInt32
    var instanceSize: UInt32
    var instanceAlignmentMask: UInt16
    var reserved: UInt16
    var classSize: UInt32
    var classAddressPoint: UInt32
    var typeDescriptor: UnsafeMutableRawPointer
    var iVarDestroyer: UnsafeRawPointer
}
class PSYModel{
  var age: Int = 18
  var name: String = "psy"
}

var t = PSYModel()
// 獲取實例對象的指針是一個原生指針類型
let objcRawPtr = Unmanaged.passUnretained(t as AnyObject).toOpaque()
// 綁定內(nèi)存
let objcPtr = objcRawPtr.bindMemory(to: HeapObject.self
    , capacity: 1)
let metadata = objcPtr.pointee.metadata.bindMemory(to: Metadata.self, capacity: MemoryLayout<Metadata>.stride).pointee
 MemoryLayout<Metadata>.stride

print("end")

下斷點麦箍,lldb輸出如下:


最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市陶珠,隨后出現(xiàn)的幾起案子挟裂,更是在濱河造成了極大的恐慌,老刑警劉巖揍诽,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诀蓉,死亡現(xiàn)場離奇詭異,居然都是意外死亡暑脆,警方通過查閱死者的電腦和手機渠啤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來添吗,“玉大人沥曹,你說我怎么就攤上這事〉” “怎么了妓美?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鲤孵。 經(jīng)常有香客問我壶栋,道長,這世上最難降的妖魔是什么普监? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任贵试,我火速辦了婚禮,結果婚禮上凯正,老公的妹妹穿的比我還像新娘毙玻。我一直安慰自己,他們只是感情好漆际,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布淆珊。 她就那樣靜靜地躺著,像睡著了一般奸汇。 火紅的嫁衣襯著肌膚如雪施符。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天擂找,我揣著相機與錄音戳吝,去河邊找鬼。 笑死贯涎,一個胖子當著我的面吹牛听哭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼陆盘,長吁一口氣:“原來是場噩夢啊……” “哼普筹!你這毒婦竟也來了?” 一聲冷哼從身側響起隘马,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤太防,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后酸员,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜒车,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年幔嗦,在試婚紗的時候發(fā)現(xiàn)自己被綠了酿愧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡邀泉,死狀恐怖嬉挡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呼渣,我是刑警寧澤棘伴,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布寞埠,位于F島的核電站屁置,受9級特大地震影響,放射性物質發(fā)生泄漏仁连。R本人自食惡果不足惜蓝角,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饭冬。 院中可真熱鬧使鹅,春花似錦、人聲如沸昌抠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炊苫。三九已至裁厅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侨艾,已是汗流浹背执虹。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留唠梨,地道東北人袋励。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親茬故。 傳聞我的和親對象是個殘疾皇子盖灸,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

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