Swift 二第队、屬性

屬性.png

一励稳、存儲屬性

存儲屬性是一個作為特定類和結(jié)構(gòu)體實例一部分的常量或變量。存儲屬性要么是變量存儲屬性 (由 var 關(guān)鍵字引入)要么是常量存儲屬性(由 let 關(guān)鍵字引入)犬金。存儲屬性這里沒有什么特別要強調(diào)的念恍,因為隨處可見。

class ZGTeacher {
    let age: Int = 32
    var name: String = "Zhang"
}

比如這里的 agename 就是我們所說的存儲屬性晚顷,這里我們需要加以區(qū)分的是 letvar 兩者的區(qū)別:從定義上: let 用來聲明常量峰伙,常量的值一旦設(shè)置好便不能再被更改;var 用來聲明變量该默,變量的值可以在將來設(shè)置為不同的值瞳氓。

1.1 代碼案例

這里我們來看幾個案例:

class ZGTeacher {
    let age: Int
    var name: String
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
}

struct ZGStudent {
    let age: Int
    var name: String
}

let t = ZGTeacher(age: 18, name: "Hello")
t.age = 20
t.name = "Logic"
t = ZGTeacher(age: 30, name: "Kody")

var t1 = ZGTeacher(age: 18, name: "Hello")
t1.age = 20
t1.name = "Logic"
t1 = ZGTeacher(age: 30, name: "Kody")

let s = ZGStudent(age: 18, name: "Hello")
s.age = 25
s.name = "Doman"
s = ZGStudent(age: 18, name: "Hello")

var s1 = ZGStudent(age: 18, name: "Hello")
s1.age = 25
s1.name = "Doman"
s1 = ZGStudent(age: 18, name: "Hello")
屬性錯誤分析.png
  • 第25行代碼:t.age因為age屬性是被let修飾的,是一個常量存儲屬性栓袖,被賦值為18后匣摘,不可以再被更改
  • 第26行代碼: t.name因為name屬性是被var修飾的,是一個變量存儲屬性裹刮,被賦值為"hello"后音榜,可以被"Logic"這個值修改替換
  • 第27行代碼: t被let修飾并賦值為ZGTeacher(age: 18, name: "Hello")后,也是一個常量存儲屬性捧弃,不可以再次更改為ZGTeacher(age: 30, name: "Kody")
  • 第30行代碼:t1.age因為age屬性是被let修飾的赠叼,是一個常量存儲屬性,被賦值為18后违霞,不可以再被更改為20
  • 第31行代碼: t1這個類是被var修飾的,t1中的name屬性也是被var修飾的,t1中的name屬性可以被再次賦值
  • 第32行代碼:t1這個類是被var修飾的嘴办,可以被再次賦值
  • 第35行代碼:s結(jié)構(gòu)體是被let修飾的,其中的age也是被let修飾的,被賦值為18后买鸽,不可以更改為25
  • 第36行代碼:s結(jié)構(gòu)體是被let修飾的,其中的name雖然是被var修飾的涧郊,但只可以被賦值一次,被賦值為"Hello"后不可以被再次賦值
  • 第37行代碼:s結(jié)構(gòu)體是被let修飾的,賦值一次后眼五,不可以再次賦值
  • 第40行代碼:s1結(jié)構(gòu)體中的age屬性是被let修飾的,賦值18后妆艘,不可以再次賦值
  • 第41行代碼:s1結(jié)構(gòu)體是被var修飾的,結(jié)構(gòu)體中的name也是被var修飾的弹砚,被賦值為"Hello"后双仍,也可以被再次賦值為"doman"
  • 第42行代碼:s1結(jié)構(gòu)體是被var修飾的枢希,可以被多次賦值

1.2 let和var的區(qū)別

1.2.1 從匯編的角度

1.png

2.png

通過上面的截圖可以看到桌吃,常量和變量的存儲沒有明顯的區(qū)別。

1.2.2 從SIL的角度

///@_hasStorage代表是存儲屬性
///@_hasInitialValue代表有初始值
///有g(shù)et和set方法
@_hasStorage @_hasInitialValue var age: Int { get set }

///只有g(shù)et方法
@_hasStorage @_hasInitialValue let x: Int { get }

代表var修飾的age是變量存儲屬性苞轿,可以調(diào)用get方法獲取值茅诱,也可以調(diào)用set方法賦值逗物,而let 修飾的x是常量存儲屬性,只可以調(diào)用get方法獲取值,一旦被賦值后不可以再更改瑟俭。

二翎卓、計算屬性

存儲的屬性是最常見的,除了存儲屬性摆寄,類失暴、結(jié)構(gòu)體和枚舉也能夠定義計算屬性,計算屬性并不存儲值微饥,他們提供 gettersetter修改和獲取值逗扒。對于存儲屬性來說可以是常量或變量,但計算屬性必須定義為變量欠橘。于此同時我們書寫計算屬性時候必須包含類型矩肩,因為編譯器需要知道期望返回值是什么。

struct square {
///實例當(dāng)中占據(jù)內(nèi)存的
    var width: Double
///本質(zhì)是一個方法肃续,不占據(jù)內(nèi)存
    var area: Double {
        get {
            return width * width
        }
        
        set {
            self.width = newValue
        }
    }
}
匯編打印.png

通過上圖可以發(fā)現(xiàn)square.area.setter黍檩,就是一個方法的靜態(tài)調(diào)用,而不是對屬性的存儲始锚。

下面我們看一下只讀的計算屬性和let 有什么區(qū)別

struct square {
    var width: Double = 30
    var area: Double {
        get {
            return width * width
        }
    }
    let height: Double = 20
}

編譯成SIL文件看一下

struct square {
  @_hasStorage @_hasInitialValue var width: Double { get set }
  var area: Double { get }
  @_hasStorage @_hasInitialValue let height: Double { get }
  init()
  init(width: Double = 30)
}

var area: Double { get }和@_hasStorage @_hasInitialValue let height: Double { get } 相同點是都有g(shù)et方法刽酱,但它們的本質(zhì)是不一樣的。area是方法瞧捌,height是let修飾的屬性肛跌。

三、屬性觀察者

屬性觀察者會用來觀察屬性值的變化察郁,一個 willSet 當(dāng)屬性將被改變調(diào)用衍慎,即使這個值與原有的值相同,而 didSet 在屬性已經(jīng)改變之后調(diào)用皮钠。它們的語法類似于 gettersetter稳捆。

class Subject {
    var subjectName: String = "" {
        ///系統(tǒng)默認(rèn)生成的newValue
        willSet {
            print("subject will set value \(newValue)")
        }
        didSet {
            print("subject will set value \(oldValue)")
        }
        
//        ///這里newBody=newValue,如果你不想用系統(tǒng)默認(rèn)創(chuàng)建的值麦轰,可以自己定義一個別名
//        willSet (newBody) {
//            print("subject will set value \(newBody)")
//        }
//        ///這里oldBody=oldBody
//        didSet(oldBody) {
//            print("subject will set value \(oldBody)")
//        }
    }
}

let s = Subject()
s.subjectName = "Swift"

這里我們在使用屬性觀察器的時候乔夯,需要注意的一點是在初始化期間設(shè)置屬性時不會調(diào)用 willSetdidSet 觀察者;只有在為完全初始化的實例分配新值時才會調(diào)用它們款侵。運行下面這段代碼末荐,你會發(fā)現(xiàn)當(dāng)前并不會有任何的輸出。

class Subject {
    var subjectName: String = "" {
        ///系統(tǒng)默認(rèn)生成的newValue
        willSet {
            print("subject will set value \(newValue)")
        }
        didSet {
            print("subject will set value \(oldValue)")
        }
        
    }
    
    init(subjectName: String) {
        ///初始化的操作
        self.subjectName = subjectName
    }
}

let s = Subject(subjectName: "Swift")

上面的屬性觀察者只是對存儲屬性起作用新锈,如果我們想對計算屬性起作用怎么辦甲脏?很簡單,只需將相關(guān)代碼添加到屬性的 setter。我們先來看這段代碼:

class Square {
    var width: Double
    var area: Double {
        get {
            return width * width
        }
        
        set {
            self.width = sqrt(newValue)
        }
        
        willSet {
            print("area will set value \(newValue)")
        }
        
        didSet {
            print("area has been changed \(oldValue)")
        }
    }
    
    init(width: Double) {
        self.width = width
    }
}

運行報錯.png

對于計算屬性的觀察者:分為基本計算屬性帶有繼承的計算屬性
因為沒有初始化方法块请,不需要添加willSet娜氏,didSet觀察者,如果想被觀察者訪問到墩新,只需要將觀察者需要實現(xiàn)的代碼添加到自己本身自帶的setter方法里贸弥。

class ZGTeacher {
    var age: Int {
        willSet {
            print("age will set value \(newValue)")
        }
    
        didSet {
            print("age has been changed \(oldValue)")
        }
    }
    var height: Double
    init(_ age: Int, _ height: Double) {
        self.age = age
        self.height = height
    }
}

class ZGPartTimeTeacher: ZGTeacher {
    override var age: Int {
        willSet {
            print("override age will set value \(newValue)")
        }
    
        didSet {
            print("override age has been changed \(oldValue)")
        }
    }
    var subjectName: String
    init(_ subjectName: String) {
        self.subjectName = subjectName
        super.init(18, 30.0)
        self.age = 20
    }
}

let t = ZGPartTimeTeacher("Swift")

打印結(jié)果如下:

override age will set value 20
age will set value 20
age has been changed 18
override age has been changed 18

可以得出以下結(jié)論:繼承屬性調(diào)用setter --> 調(diào)用自身willset --> 調(diào)用父類setter --> 父類調(diào)用自身willset --> 賦值 --> 父類調(diào)用自身didset --> 繼承屬性調(diào)用自身didset

四、延遲存儲屬性

  • 延遲存儲屬性的初始值在其第一次使用時才進行計算海渊。
  • 用關(guān)鍵字lazy 來標(biāo)識一個延遲存儲屬性绵疲。
class Subject {
    lazy var age: Int = 18
}

var s = Subject()
print(s.age)
print("end")

我們來看下lldb的對應(yīng)打印

po s
<Subject: 0x101b32fc0>

(lldb) x/8g 0x101b32fc0
0x101b32fc0: 0x0000000100008160 0x0000000200000003
0x101b32fd0: 0x0000000000000000 0x0000000101b33101
0x101b32fe0: 0x0000000000000000 0x0000000000000000
0x101b32ff0: 0x0000000101b30006 0x0000000100000001
18
(lldb) po s
<Subject: 0x101b32fc0>

(lldb) x/8g 0x101b32fc0
0x101b32fc0: 0x0000000100008160 0x0000000400000003
0x101b32fd0: 0x0000000000000012 0x0000000101b33100
0x101b32fe0: 0x0000000000000000 0x0000000000000000
0x101b32ff0: 0x0000000101b30006 0x0000000100000001

這是一個存儲屬性,前16個字節(jié)存儲我們的Metadata(0x0000000100008160)refro(0x0000000400000003)
16個字節(jié)后開始存儲屬性對應(yīng)的值0x0000000000000000
過掉s.age斷點值存儲進去變?yōu)?strong>0x0000000000000012臣疑,所以age被lazy修飾后最岗,所以它是在第一次訪問之后才會對它進行初始化操作。
那么添加lazy和不添加lazy對我們的內(nèi)存大小有沒有影響哪朝捆?我們把代碼編譯成sil文件看一下般渡。

class Subject {
  lazy var age: Int { get set }
  @_hasStorage @_hasInitialValue final var $__lazy_storage_$_age: Int? { get set }
  @objc deinit
  init()
}

可以看到Int?,意味著添加了lazy以后芙盘,屬性被修飾成了一個可選類型驯用。

// variable initialization expression of Subject.$__lazy_storage_$_age
sil hidden [transparent] @$s4main7SubjectC21$__lazy_storage_$_age029_12232F587A4C5CD8B1EEDF696793G2FCLLSiSgvpfi : $@convention(thin) () -> Optional<Int> {
bb0:
  %0 = enum $Optional<Int>, #Optional.none!enumelt // user: %1
  return %0 : $Optional<Int>                      // id: %1
}

lazy_storage初始化表達(dá)式中 給了一個枚舉值Optional.none!enumelt ,可以看作OC中的nil

%18 = class_method %15 : $Subject, #Subject.age!getter : (Subject) -> () -> Int, $@convention(method) (@guaranteed Subject) -> Int // user: %19

Subject.age!getter調(diào)用age的getter方法, class_method是一個vtable函數(shù)表的調(diào)用。

// Subject.age.getter
sil hidden [lazy_getter] [noinline] @$s4main7SubjectC3ageSivg : $@convention(method) (@guaranteed Subject) -> Int {
// %0 "self"                                      // users: %14, %2, %1
bb0(%0 : $Subject):
  debug_value %0 : $Subject, let, name "self", argno 1 // id: %1
  %2 = ref_element_addr %0 : $Subject, #Subject.$__lazy_storage_$_age // user: %3
  %3 = begin_access [read] [dynamic] %2 : $*Optional<Int> // users: %4, %5
  %4 = load %3 : $*Optional<Int>                  // user: %6
  end_access %3 : $*Optional<Int>                 // id: %5
  switch_enum %4 : $Optional<Int>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 // id: %6

Subject.age.setter方法首先訪問我們的Subject.$__lazy_storage_$_age地址ref_element_addr儒老,然后將這個地址的值給到我們的常量寄存器%4, switch_enum %4進行一個枚舉模式的匹配蝴乔,如果有值,走bb1代碼塊驮樊,沒值薇正,就走bb2代碼塊。

延遲存儲屬性的初始值在其第一次使用時才進行計算囚衔,相當(dāng)于節(jié)省了我們的內(nèi)存空間挖腰。我們的延遲存儲屬性并不是線程安全的,這點大家要注意练湿。

五猴仑、類型屬性

  • 類型屬性其實就是一個全局變量
class ZGTeacher {
    static var age: Int = 18
}

類型屬性的調(diào)用方式是 類名+屬性,如上面代碼調(diào)用ZGTeacher.age
我們轉(zhuǎn)譯成sil文件看一下上面的代碼

class ZGTeacher {
  @_hasStorage @_hasInitialValue static var age: Int { get set }
  @objc deinit
  init()
}

// one-time initialization token for age
sil_global private @$s4main9ZGTeacherC3age_Wz : $Builtin.Word

// static ZGTeacher.age
sil_global hidden @$s4main9ZGTeacherC3ageSivpZ : $Int

我們看sil文件代碼,發(fā)現(xiàn)生成了一個token和一個static修飾的全局變量肥哎。
所以看到這里辽俗,我們應(yīng)該可以理解static本質(zhì)上就是一個全局變量。

  • 類型屬性只會被初始化一次
// ZGTeacher.age.unsafeMutableAddressor
sil hidden [global_init] @$s4main9ZGTeacherC3ageSivau : $@convention(thin) () -> Builtin.RawPointer {
bb0:
  %0 = global_addr @$s4main9ZGTeacherC3age_Wz : $*Builtin.Word // user: %1
  %1 = address_to_pointer %0 : $*Builtin.Word to $Builtin.RawPointer // user: %3
  // function_ref one-time initialization function for age
  %2 = function_ref @$s4main9ZGTeacherC3age_WZ : $@convention(c) () -> () // user: %3
  %3 = builtin "once"(%1 : $Builtin.RawPointer, %2 : $@convention(c) () -> ()) : $()
  %4 = global_addr @$s4main9ZGTeacherC3ageSivpZ : $*Int // user: %5
  %5 = address_to_pointer %4 : $*Int to $Builtin.RawPointer // user: %6
  return %5 : $Builtin.RawPointer                 // id: %6
}
// one-time initialization function for age
sil private [global_init_once_fn] @$s4main9ZGTeacherC3age_WZ : $@convention(c) () -> () {
bb0:
  alloc_global @$s4main9ZGTeacherC3ageSivpZ       // id: %0
  %1 = global_addr @$s4main9ZGTeacherC3ageSivpZ : $*Int // user: %4
  %2 = integer_literal $Builtin.Int64, 18         // user: %3
  %3 = struct $Int (%2 : $Builtin.Int64)          // user: %4
  store %3 to %1 : $*Int                          // id: %4
  %5 = tuple ()                                   // user: %6
  return %5 : $()                                 // id: %6
}

builtin "once"表示執(zhí)行一次
我們降級成IR文件再來看一下

once_not_done:                                    ; preds = %entry
  call void @swift_once(i64* @"$s4main9ZGTeacherC3age_Wz", i8* bitcast (void ()* @"$s4main9ZGTeacherC3age_WZ" to i8*), i8* undef)
  br label %once_done
}

這里調(diào)用了@swift_once篡诽,那么@swift_once是做什么的哪崖飘,我們到Swift源碼搜一下。我們在once.cpp文件中看到如下代碼:

void swift::swift_once(swift_once_t *predicate, void (*fn)(void *),
                       void *context) {
#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME
  if (! *predicate) {
    *predicate = true;
    fn(context);
  }
#elif defined(__APPLE__)
  dispatch_once_f(predicate, context, fn);
#elif defined(__CYGWIN__)
  _swift_once_f(predicate, context, fn);
#else
  std::call_once(*predicate, [fn, context]() { fn(context); });
#endif

可以看出杈女,是調(diào)用了GCD朱浴,確保只被初始化一次吊圾。只初始化一次,就和我們的OC中的單例很像赊琳。
我們先來回顧一下OC單例:

+ (instancetype)sharedInstance {
    static Test1 * t = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        t = [[Test1 alloc]init];
    });
    return t;
}

那么Swift中的單例又是怎么樣的哪?

class ZGTest {
    static let sharedInstance = ZGTest()
    private init(){}
}

六砰碴、屬性在Mach-o文件中的位置信息

在第一節(jié)課的過程中我們講到了 Metadata 的元數(shù)據(jù)結(jié)構(gòu)躏筏,我們回顧一下

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

}

上一節(jié)課講到方法調(diào)度的過程中我們認(rèn)識了 typeDescriptor ,這里面記錄了 V-Table 的相關(guān) 信息呈枉,接下來我們需要認(rèn)識一下 typeDescriptor 中的 fieldDescriptor趁尼。

struct TargetClassDescriptor {
    var flags: UInt32
    var parent: UInt32
    var name: Int32
    var accessFunctionPointer: Int32
    var fieldDescriptor: Int32
    var superClassType: Int32
    var metadataNegativeSizeInWords: UInt32
    var metadataPositiveSizeInWords: UInt32
    var numImmediateMembers: UInt32
    var numFields: UInt32
    var fieldOffsetVectorOffset: UInt32
    var Offset: UInt32
    var size: UInt32
    //V-Table
}

上節(jié)課我們提到,我們的typeDescriptor存儲在__TEXT,__Swift_types里,將自己項目的mach-o文件放到machoView應(yīng)用里猖辫,可以看到:

typeDescriptor地址.png

fieldDescriptor 記錄了當(dāng)前的屬性信息酥泞,其中fieldDescriptor在源碼中的結(jié)構(gòu)如下:

struct FieldDescriptor {
    MangledTypeName int32
    Superclass int32
    Kind uint16
    FieldRecordSize uint16
    NumFields uint32
    FieldRecords [FieldRecord]
}

我們用 0xFFFFFF2C+0x00003F4C得到0x100003E78,這個就是typeDescriptor在當(dāng)前Mach-o文件中的地址啃憎,減去我們的虛擬內(nèi)存地址0x100000000芝囤,得到一個0x3E78,我們可以直接定位到該地址

image.png

看一下這個typeDescriptor結(jié)構(gòu)體辛萍,如果我們想要找到fieldDescriptor地址悯姊,需要從當(dāng)前地址偏移4個4字節(jié)。
image.png

這個9C存儲的是偏移信息贩毕,所以我們用0x00003E88加上偏移信息
0x9C得到0x3F24
image.png

這個地址0x3F24就就代表了我們的fieldDescriptor悯许,而它后面的地址就是這個結(jié)構(gòu)體存儲的內(nèi)容。

其中 NumFields 代表當(dāng)前有多少個屬性辉阶, FieldRecords 記錄了每個屬性的信息先壕,FieldRecords 的結(jié)構(gòu)體如下:

struct FieldRecord {
    Flags uint32
    MangledTypeName int32
    FieldName int32
}

我們想要拿到FieldRecords的地址,只需要0x3F24偏移4個4字節(jié)谆甜。

image.png

我們想要拿到FieldRecords結(jié)構(gòu)體中的FieldName來驗證一下拿到的對不對垃僚,那么我們只需要偏移2個4字節(jié)。用0x00003F34 + 0x8得到0x00003F3C再加上0xFFFFFFDD规辱,得到0x100003F19冈在,減去我們的虛擬內(nèi)存地址0x100000000,得到0x3F19
image.png

是的,這里我們拿到并驗證了age和age1兩個屬性存放的地址按摘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末包券,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子炫贤,更是在濱河造成了極大的恐慌溅固,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兰珍,死亡現(xiàn)場離奇詭異侍郭,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門亮元,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猛计,“玉大人,你說我怎么就攤上這事爆捞》盍觯” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵煮甥,是天一觀的道長盗温。 經(jīng)常有香客問我,道長成肘,這世上最難降的妖魔是什么卖局? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮双霍,結(jié)果婚禮上砚偶,老公的妹妹穿的比我還像新娘。我一直安慰自己洒闸,他們只是感情好蟹演,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著顷蟀,像睡著了一般酒请。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸣个,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天羞反,我揣著相機與錄音,去河邊找鬼囤萤。 笑死昼窗,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的涛舍。 我是一名探鬼主播澄惊,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼富雅!你這毒婦竟也來了掸驱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤没佑,失蹤者是張志新(化名)和其女友劉穎毕贼,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛤奢,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡鬼癣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年陶贼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片待秃。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拜秧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出章郁,到底是詐尸還是另有隱情枉氮,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布驱犹,位于F島的核電站嘲恍,受9級特大地震影響足画,放射性物質(zhì)發(fā)生泄漏雄驹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一淹辞、第九天 我趴在偏房一處隱蔽的房頂上張望医舆。 院中可真熱鬧,春花似錦象缀、人聲如沸蔬将。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霞怀。三九已至,卻和暖如春莉给,著一層夾襖步出監(jiān)牢的瞬間毙石,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工颓遏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徐矩,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓叁幢,卻偏偏與公主長得像滤灯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子曼玩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

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