Swift進階06:內存管理 & Runtime

本文主要介紹Swift中的內存管理,涉及引用計數(shù)盖桥、弱引用、強引用题翻、循環(huán)引用揩徊、Runtime等

內存管理 - 強引用

在Swift中也是使用自動引用計數(shù)(ARC)機制來追蹤和管理內存的,下面我們通過一個案例來進行分析

class HTTeacher {
    var name: String = "teacher"
    var age: Int = 18
}
var t = HTTeacher()
var t1 = t
var t2 = t
  • 通過LLDB指令查看t的內存情況 藐握,為什么其中的refCounts是0x0000000600000002靴拱?
image

源碼分析

在分析類時(參考這篇文章Swift進階02: 類、對象猾普、屬性)有這么一個類HeapObject,下面繼續(xù)通過這個類來分析t的引用計數(shù)

  • 分析源碼 HeapObject -> InlineRefCounts
struct HeapObject {
  HeapMetadata const *metadata;

  SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
  ...
}
??
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS       \
  InlineRefCounts refCounts
  • 進入InlineRefCounts定義本谜,是RefCounts類型的別名初家,而RefCounts是模板類,真正決定的是傳入的類型InlineRefCountBits
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
??
template <typename RefCountBits>
class RefCounts {
  std::atomic<RefCountBits> refCounts;
  ...
}
  • 分析InlineRefCountBits乌助,是RefCountBitsT類的別名
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
  • 分析RefCountBitsT溜在,有bits屬性
template <RefCountInlinedness refcountIsInline>
class RefCountBitsT {
    ...
      typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
    BitsType;
    ...
    BitsType bits;
    ...
}
??
template <>
struct RefCountBitsInt<RefCountNotInline, 4> {
  //類型
  typedef uint64_t Type;
  typedef int64_t SignedType;
};

其中bits其實質是將RefCountBitsInt中的type屬性取了一個別名,所以bits的真正類型是uint64_t64位整型數(shù)組

然后來繼續(xù)分析swift中對象創(chuàng)建的底層方法swift_allocObject

  • 分析初始化源碼swift_allocObject
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
                                       size_t requiredSize,
                                       size_t requiredAlignmentMask) {
    ...
    new (object) HeapObject(metadata);
    ...
}
??
<!--構造函數(shù)-->
  // Initialize a HeapObject header as appropriate for a newly-allocated object.
 constexpr HeapObject(HeapMetadata const *newMetadata) 
    : metadata(newMetadata)
    , refCounts(InlineRefCounts::Initialized)
  { }
  • 進入Initialized定義他托,是一個枚舉掖肋,其對應的refCounts方法中,
  enum Initialized_t { Initialized };
  
  //對應的RefCounts方法
// Refcount of a new object is 1.
  constexpr RefCounts(Initialized_t)
    : refCounts(RefCountBits(0, 1)) {}

從這里看出真正起作用的是 RefCountBits

  • 進入RefCountBits定義赏参,也是一個模板定義
template <typename RefCountBits>
class RefCounts {
  std::atomic<RefCountBits> refCounts;
  ...
}

所以真正的初始化地方是下面這個志笼,實際上是做了一個位域操作沿盅,根據(jù)的是Offsets

  LLVM_ATTRIBUTE_ALWAYS_INLINE
  constexpr
  RefCountBitsT(uint32_t strongExtraCount, uint32_t unownedCount)
    : bits((BitsType(strongExtraCount) << Offsets::StrongExtraRefCountShift) |
           (BitsType(1)                << Offsets::PureSwiftDeallocShift) |
           (BitsType(unownedCount)     << Offsets::UnownedRefCountShift))
  { }

分析RefCountsBit的結構,如下所示纫溃,

image

  • isImmortal(0)
  • UnownedRefCount(1-31): unowned的引用計數(shù)
  • isDeinitingMask(32):是否進行釋放操作
  • StrongExtraRefCount(33-62): 強引用計數(shù)
  • UseSlowRC(63)

重點關注UnownedRefCountStrongExtraRefCount

  • 將t的refCounts用二進制展示腰涧,其中強引用計數(shù)為3
image

分析SIL代碼

  • 當只有t實例變量時
image
  • 當有t + t1時,查看是否有 strong_retain操作
//SIL中的main
alloc_global @main.t1 : main.HTTeacher         // id: %8
  %9 = global_addr @main.t1 : main.HTTeacher : $*HTTeacher // user: %11
  %10 = begin_access [read] [dynamic] %3 : $*HTTeacher // users: %12, %11
  copy_addr %10 to [initialization] %9 : $*HTTeacher // id: %11
  
//其中copy_addr等價于
- %new = load s*HTTeacher
- strong_retain %new
- store %new to %9
image

SIL官方文檔中關于 copy_addr的解釋如下

image

  • 其中的strong_retain對應的就是 swift_retain紊浩,其內部是一個宏定義窖铡,內部是_swift_retain_,其實現(xiàn)是對object的引用計數(shù)作+1操作
//內部是一個宏定義
HeapObject *swift::swift_retain(HeapObject *object) {
  CALL_IMPL(swift_retain, (object));
}
??
//本質調用的就是 _swift_retain_
static HeapObject *_swift_retain_(HeapObject *object) {
  SWIFT_RT_TRACK_INVOCATION(object, swift_retain);
  if (isValidPointerForNativeRetain(object))
    object->refCounts.increment(1);
  return object;
}
??
void increment(uint32_t inc = 1) {
    auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
    
    // constant propagation will remove this in swift_retain, it should only
    // be present in swift_retain_n
    if (inc != 1 && oldbits.isImmortal(true)) {
      return;
    }
    //64位bits
    RefCountBits newbits;
    do {
      newbits = oldbits;
      bool fast = newbits.incrementStrongExtraRefCount(inc);
      if (SWIFT_UNLIKELY(!fast)) {
        if (oldbits.isImmortal(false))
          return;
        return incrementSlow(oldbits, inc);
      }
    } while (!refCounts.compare_exchange_weak(oldbits, newbits,
                                              std::memory_order_relaxed));
  }
  • 回退到HeapObject坊谁,從InlineRefCounts進入费彼,其中是c++中的模板定義,是為了更好的抽象口芍,在其中查找bits(即decrementStrongExtraRefCount方法)
LLVM_NODISCARD LLVM_ATTRIBUTE_ALWAYS_INLINE
bool incrementStrongExtraRefCount(uint32_t inc) {
// This deliberately overflows into the UseSlowRC field.
// 對inc做強制類型轉換為 BitsType
// 其中 BitsType(inc) << Offsets::StrongExtraRefCountShift 等價于 1<<33位敌买,16進制為 0x200000000
//這里的 bits += 0x200000000,將對應的33-63轉換為10進制阶界,為
bits += BitsType(inc) << Offsets::StrongExtraRefCountShift;
return (SignedBitsType(bits) >= 0);
}

例如以trefCounts為例(其中62-33位是strongCount虹钮,每次增加強引用計數(shù)增加都是在33-62位上增加的,固定的增量為1左移33位膘融,即0x200000000

  • 只有t時的refCounts是 0x0000000200000002
  • t + t1時的refCounts是 0x0000000400000002 = 0x0000000200000002 + 0x200000000
  • t + t1 + t2 時的refCounts是 0x0000000600000002 = 0x0000000400000002 + 0x200000000
  • 針對上面的例子芙粱,可以通過CFGetRetainCOunt獲取引用計數(shù),發(fā)現(xiàn)依次是 2、3氧映、4春畔,默認多了一個1(調用CFGetRetainCOunt時會增加1)
image
  • 如果將t、t1岛都、t2放入函數(shù)中律姨,還會再次retain一次
image

為什么是0x200000000
因為1左移33位臼疫,其中4位為一組择份,計算成16進制,剩余的33-32位0x10烫堤,轉換為10進制為2荣赶。其實際增加引用技術就是1

swift與OC強引用計數(shù)對比

  • OC中創(chuàng)建實例對象時為0
  • swift中創(chuàng)建實例對象時默認為1

內存管理 - 弱引用

以下面為例:

class HTTeacher {
    var name: String = "teacher"
    var age: Int = 18
    var stu: HTStudent?
}

class HTStudent {
    var age = 18
    var teacher: HTTeacher?
}

func test() {
    var t = HTTeacher()
    weak var t1 = t
    
    print("end")
}
  • 查看t的引用計數(shù)變化
image
  • 弱引用聲明的變量是一個可選值,因為在程序運行過程中是允許將當前變量設置為nil
image
  • weak var t1 = t處加斷點鸽斟,查看匯編
image
  • 源碼查看 swift_weakInit函數(shù)拔创,這個函數(shù)是由WeakReference來調用的,相當于weak字段在編譯器聲明過程中就自定義了一個WeakReference的對象富蓄,其目的在于管理弱引用
WeakReference *swift::swift_weakInit(WeakReference *ref, HeapObject *value) {
  ref->nativeInit(value);
  return ref;
}
  • 進入nativeInit
void nativeInit(HeapObject *object) {
auto side = object ? object->refCounts.formWeakReference() : nullptr;
nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed);
}
  • 進入formWeakReference剩燥,創(chuàng)建sideTable,
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::formWeakReference()
{
  //創(chuàng)建 sideTable
  auto side = allocateSideTable(true);
  if (side)
  // 如果創(chuàng)建成功立倍,則增加弱引用
    return side->incrementWeak();
  else
    return nullptr;
}
  • 進入allocateSideTable
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::allocateSideTable(bool failIfDeiniting)
{
  // 1灭红、先拿到原本的引用計數(shù)
  auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
  
  // Preflight failures before allocating a new side table.
  if (oldbits.hasSideTable()) {
    // Already have a side table. Return it.
    return oldbits.getSideTable();
  } 
  else if (failIfDeiniting && oldbits.getIsDeiniting()) {
    // Already past the start of deinit. Do nothing.
    return nullptr;
  }

  // Preflight passed. Allocate a side table.
  
  // FIXME: custom side table allocator
  //2侣滩、創(chuàng)建sideTable
  HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
  // 3、將創(chuàng)建的地址給到InlineRefCountBits
  auto newbits = InlineRefCountBits(side);
  
  do {
    if (oldbits.hasSideTable()) {
      // Already have a side table. Return it and delete ours.
      // Read before delete to streamline barriers.
      auto result = oldbits.getSideTable();
      delete side;
      return result;
    }
    else if (failIfDeiniting && oldbits.getIsDeiniting()) {
      // Already past the start of deinit. Do nothing.
      return nullptr;
    }
    
    side->initRefCounts(oldbits);
    
  } while (! refCounts.compare_exchange_weak(oldbits, newbits,
                                             std::memory_order_release,
                                             std::memory_order_relaxed));
  return side;
}
  • 1比伏、先拿到原本的引用計數(shù)
  • 2胜卤、創(chuàng)建sideTable
  • 3、將創(chuàng)建的sideTable地址給InlineRefCountBits赁项,并查看其初始化方法葛躏,根據(jù)sideTable地址做了偏移操作并存儲到內存,相當于將sideTable直接存儲到了64位的變量中
image

所以上面的0xc00000002008d8aeHeapObjectSideTableEntry實例對象的內存地址悠菜,即散列表的地址(除去63舰攒、62位)

  • 查看HeapObjectSideTableEntry定義,其中有object對象悔醋、refCounts
image
  • 進入SideTableRefCounts摩窃,同InlineRefCounts類似,實際做事的是SideTableRefCountBits芬骄,繼承自RefCountBitsT(存的是uint64_t類型的64位的信息)猾愿,還有一個uint32_tweakBits,即32位的位域信息
    • 64位 用于記錄 原有引用計數(shù)
    • 32位 用于記錄 弱引用計數(shù)
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts;
image

0xc00000002008f0cc為例账阻,將62蒂秘、63位清零,變成0x2008F0CC淘太,然后左移3位(即InlineRefCountBits初始化方法)姻僧,變成0x100478660HeapObjectSideTableEntry對象地址,即散列表地址蒲牧,然后通過x/8g讀取

image

問題:如果此時再加一個強引用t2

查看其refCounts撇贺,t2是執(zhí)行了strong_retain

image

  • 源碼查看 _swift_retain_ -> increment -> incrementSlow -> incrementStrong
image

總結

對于HeapObject來說,其refCounts有兩種:

  • 無弱引用:strongCount + unownedCount
  • 有弱引用:object + xxx + (strongCount + unownedCount) + weakCount
HeapObject {
    InlineRefCountBit {strong count + unowned count }
    
    HeapObjectSideTableEntry{
        HeapObject *object
        xxx
        strong Count + unowned Count(uint64_t)//64位
        weak count(uint32_t)//32位
    }
}

內存管理 - 循環(huán)引用

主要是研究閉包捕獲外部變量冰抢,以下面代碼為例

func test() {
    var age = 10
    let clourse = {
        age += 1
    }
    clourse()
    print(age)
}

test()

從輸出結果中可以看出:閉包內部對變量的修改將會改變外部原始變量的值,主要原因是閉包會捕獲外部變量松嘶,這個與OC中的block是一致的

  • 定義一個類,在test函數(shù)作用域消失后晒屎,會執(zhí)行deinit
class HTTeacher {
    var age = 10
    //反初始化器(當前實例對象被回收時調用)
    deinit {
        print("HTTeacher deinit")
    }
}

func test() {
    var t = HTTeacher()
}

test()
  • 【修改1】修改例子喘蟆,通過閉包修改其屬性值
class HTTeacher {
    var age = 10
    //反初始化器(當前實例對象被回收時調用)
    deinit {
        print("HTTeacher deinit")
    }
}

func test() {
    var t = HTTeacher()
    let clourse = {
        t.age += 1
    }
    clourse()
    print(t.age)
}

test()

運行結果發(fā)現(xiàn),閉包對 t 并沒有強引用鼓鲁,deinit方法會執(zhí)行

  • 【修改2】繼續(xù)修改例子為如下,是否有強引用港谊?
class HTTeacher {
    var age = 10
    var completionBlock: (() ->())?
    //反初始化器(當前實例對象被回收時調用)
    deinit {
        print("HTTeacher deinit")
    }
}

func test() {
    var t = HTTeacher()
    t.completionBlock = {
        t.age += 1
    }
    print(t.age)
}

test()

從運行結果發(fā)現(xiàn)骇吭,沒有執(zhí)行deinit方法,即沒有打印HTTeacher deinit歧寺,所以這里有循環(huán)引用

image

循環(huán)引用解決方法

有兩種方式可以解決swift中的循環(huán)引用

  • 【方式一】使用weak修飾閉包傳入的參數(shù)燥狰,其中參數(shù)的類型是optional
 func test() {
    var t = HTTeacher()
    t.completionBlock = { [weak t] in
        t?.age += 1
    }
    print(t.age)
}
  • 【方式二】使用無主引用 unowned修飾閉包參數(shù)棘脐,與weak的區(qū)別在于unowned不允許被設置為nil,即總是假定有值的
func test() {
    var t = HTTeacher()
    t.completionBlock = { [unowned t] in
        t.age += 1
    }
    print(t.age)
}

捕獲列表

  • [weak t] / [unowned t] 在swift中被稱為捕獲列表
  • 定義在參數(shù)列表之前
  • 【書寫方式】捕獲列表被寫成用逗號括起來的表達式列表龙致,并用方括號括起來
  • 如果使用捕獲列表蛀缝,則即使省略參數(shù)名稱、參數(shù)類型和返回類型目代,也必須使用in關鍵字
  • [weak t] 就是取t的弱引用對象 類似weakself

請問下面代碼的clourse()調用后屈梁,輸出的結果是什么?

func test(){
    var age = 0
    var height = 0.0
    //將變量age用來初始化捕獲列表中的常量age榛了,即將0給了閉包中的age(值拷貝)
    let clourse = { [age] in
        print(age)
        print(height)
    }
    age = 10
    height = 1.85
    clourse()
}

test()

// 打印結果 0   1.85

所以從結果中可以得出:對于捕獲列表中的每個常量在讶,閉包會利用周圍范圍內具有相同名稱的常量/變量,來初始化捕獲列表中定義的常量霜大。有以下幾點說明:

  • 捕獲列表中的常量是值拷貝构哺,而不是引用
  • 捕獲列表中的常量的相當于復制了變量age的值
  • 捕獲列表中的常量是只讀的,即不可修改

swift中Runtime探索

  • 對于純swift類來說战坤,沒有 動態(tài)特性dynamic(因為swift靜態(tài)語言)曙强,方法和屬性不加任何修飾符的情況下,已經(jīng)不具備runtime特性途茫,此時的方法調度碟嘴,依舊是函數(shù)表調度即V_Table調度
  • 對于純swift類,方法和屬性添加@objc標識的情況下慈省,可以通過runtime API獲取到臀防,但是在OC中是無法進行調度的,原因是因為swift.h文件中沒有swift類的聲明
  • 對于繼承自NSObject類來說边败,如果想要動態(tài)的獲取當前屬性+方法袱衷,必須在其聲明前添加 @objc關鍵字,如果想要使用方法交換笑窜,還必須在屬性+方法前添加dynamic關鍵字致燥,否則當前屬性+方法只是暴露給OC使用,而不具備任何動態(tài)特性

元類型排截、AnyClass嫌蚤、Self

AnyObject

  • AnyObject:代表任意類的instance、類的類型断傲、僅類遵守的協(xié)議
class HTTeacher: NSObject {
    var age: Int = 18
}

var t = HTTeacher()

//此時代表的就是當前HTTeacher的實例對象
var t1: AnyObject = t

//此時代表的是HTTeacher這個類的類型
var t2: AnyObject = HTTeacher.self

//繼承自AnyObject脱吱,表示JSONMap協(xié)議只有類才可以遵守
protocol JSONMap: AnyObject { }

例如如果是結構體遵守協(xié)議,會報錯


image

需要將struct修改成class

//繼承自AnyObject认罩,表示JSONMap協(xié)議只有類才可以遵守
protocol JSONMap: AnyObject { }

class HTJSONMap: JSONMap {
    
}

Any

  • Any:代表任意類型箱蝠,包括 function類型 或者Optional類型,可以理解為AnyObjectAny的子集
//如果使用AnyObject會報錯,而Any不會
var array: [Any] = [1, "name", "", true]

AnyClass

  • AnyClass:代表任意實例的類型 ,類型是AnyObject.Type
    • 查看定義,是public typealias AnyClass = AnyObject.Type

T.self & T.Type

  • T.self:
    • 如果T實例對象宦搬,返回的就是它本身
    • 如果T牙瓢,那么返回的是MetaData
  • T.Type:一種類型
  • T.selfT.Type類型
//此時的tSelf類型是  HTTeacher.Type
var tSelf = HTTeacher.self
image
  • 查看t1、t2存儲的是什么间校?
class HTTeacher: NSObject {
    var age: Int = 18
}

var t = HTTeacher()
//實例對象地址:實例對象.self 返回實例對象本身
var t1 = t.self
//存儲metadata元類型
var t2 = HTTeacher.self
image

type(of:)

  • type(of:):用來獲取一個值的動態(tài)類型
<!--demo1-->
var age = 10 as NSNumber
print(type(of: age))

<!--打印結果-->
__NSCFNumber

<!--demo2-->
//value - static type 靜態(tài)類型:編譯時期確定好的
//type(of:) - dynamic type:Int
var age = 10
//value的靜態(tài)類型就是Any
func test(_ value: Any){
    
    print(type(of: value))
}

test(age)

<!--打印結果-->
Int

總結

  • 當無弱引用時矾克,HeapObject中的refCounts等于 strongCount + unownedCount
  • 當有弱引用時,HeapObject中的refCounts等于 object + xxx + (strongCount + unownedCount) + weakCount
  • 循環(huán)引用用可以通過weak / unowned修飾參數(shù)來解決
  • swift中閉包的捕獲列表值拷貝憔足,即深拷貝胁附,是一個只讀的常量
  • swift由于是靜態(tài)語言,所以屬性四瘫、方法在不加任何修飾符的情況下時是不具備動態(tài)性即Runtime特性的汉嗽,此時的方法調度是V-Table函數(shù)表調度
  • 如果想要OC使用swift類中的方法、屬性找蜜,需要class繼承NSObject饼暑,并使用@objc修飾
  • 如果想要使用方法交換,除了繼承NSObject+@objc修飾洗做,還必須使用dynamic修飾
  • Any:任意類型弓叛,包括function類型、optional類型
  • AnyObject:任意類的instance诚纸、類的類型撰筷、僅類遵守的協(xié)議,可以看作是Any的子類
  • AnyClass:任意實例類型畦徘,類型是AnyObject.Type
  • T.self:如果T是實例對象毕籽,則表示它本身,如果是類井辆,則表示metadata T.self的類型是T.Type
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末关筒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杯缺,更是在濱河造成了極大的恐慌蒸播,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萍肆,死亡現(xiàn)場離奇詭異袍榆,居然都是意外死亡,警方通過查閱死者的電腦和手機塘揣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門包雀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人亲铡,你說我怎么就攤上這事馏艾±筒埽” “怎么了奴愉?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵琅摩,是天一觀的道長。 經(jīng)常有香客問我锭硼,道長房资,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任檀头,我火速辦了婚禮轰异,結果婚禮上,老公的妹妹穿的比我還像新娘暑始。我一直安慰自己搭独,他們只是感情好,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布廊镜。 她就那樣靜靜地躺著牙肝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嗤朴。 梳的紋絲不亂的頭發(fā)上配椭,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音雹姊,去河邊找鬼股缸。 笑死,一個胖子當著我的面吹牛吱雏,可吹牛的內容都是我干的敦姻。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼歧杏,長吁一口氣:“原來是場噩夢啊……” “哼镰惦!你這毒婦竟也來了?” 一聲冷哼從身側響起得滤,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤陨献,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后懂更,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眨业,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年沮协,在試婚紗的時候發(fā)現(xiàn)自己被綠了龄捡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡慷暂,死狀恐怖聘殖,靈堂內的尸體忽然破棺而出晨雳,到底是詐尸還是另有隱情,我是刑警寧澤奸腺,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布餐禁,位于F島的核電站,受9級特大地震影響突照,放射性物質發(fā)生泄漏帮非。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一讹蘑、第九天 我趴在偏房一處隱蔽的房頂上張望末盔。 院中可真熱鬧,春花似錦座慰、人聲如沸陨舱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽游盲。三九已至,卻和暖如春邦尊,著一層夾襖步出監(jiān)牢的瞬間背桐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工蝉揍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留链峭,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓又沾,卻偏偏與公主長得像弊仪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子杖刷,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內容