Weak詳解

weak是弱引用党涕,用weak描述修飾或者所引用對象的計(jì)數(shù)器不會加一瘦真,并且會在引用的對象被釋放的時候自動被設(shè)置為nil,大大避免了野指針訪問壞內(nèi)存引起崩潰的情況眠寿,它主要用于解決循環(huán)引用型酥。

NSObject *obj = [[NSObject alloc] init];
__weak id obj1 = obj;
obj1 = @"123";

...
0000000100000efd         call       imp___stubs__objc_initWeak
0000000100000f02         lea        rdi, qword [rbp+var_20]                     ; argument "addr" for method imp___stubs__objc_storeWeak
0000000100000f06         lea        rsi, qword [cfstring_123]                   ; @"123", argument "value" for method imp___stubs__objc_storeWeak
0000000100000f0d         mov        qword [rbp+var_28], rax
0000000100000f11         call       imp___stubs__objc_storeWeak
0000000100000f16         lea        rdi, qword [rbp+var_20]                     ; argument "instance" for method imp___stubs__objc_destroyWeak
0000000100000f1a         mov        dword [rbp+var_4], 0x0
0000000100000f21         mov        qword [rbp+var_30], rax
0000000100000f25         call       imp___stubs__objc_destroyWeak
...

從上面代碼可以看出創(chuàng)建weak是通過objc_initWeak方法山憨,賦值是通過objc_storeWeak,跟屬性用weak關(guān)鍵字調(diào)用的同一個方法弥喉,銷毀是通過objc_destroyWeak方法郁竟。

id objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }
    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

id objc_storeWeak(id *location, id newObj)
{
    return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object *)newObj);
}

從上面可以看出weak的核心都是調(diào)用storeWeak方法,區(qū)別是模板的幾個參數(shù)由境,創(chuàng)建是沒有舊值得枪孩,之后賦值都是有舊值得。

template <HaveOld haveOld, HaveNew haveNew,
          CrashIfDeallocating crashIfDeallocating>
static id  storeWeak(id *location, objc_object *newObj)
{
    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;
 retry:
    if (haveOld) {
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
   //舊值改變了,就再走一遍
    if (haveOld  &&  *location != oldObj) {
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        // 判斷 isa 非空且已經(jīng)初始化
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));
            previouslyInitializedClass = cls;
            goto retry;
        }
    }
    
    if (haveOld) {//清理舊值
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }
    if (haveNew) {
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);
        // weak_register_no_lock returns nil if weak store should be rejected
        // Set is-weakly-referenced bit in refcount table.
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }
        // Do not set *location anywhere else. That would introduce a race.
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
    return (id)newObj;
}

這個函數(shù)首先引入了第一個結(jié)構(gòu)SideTable蔑舞,還可以通過SideTables得到這個結(jié)構(gòu)的對象,看下他們的結(jié)構(gòu)嘹屯。

alignas(StripedMap<SideTable>) static uint8_t 
    SideTableBuf[sizeof(StripedMap<SideTable>)];
static void SideTableInit() {
    new (SideTableBuf) StripedMap<SideTable>();
}
static StripedMap<SideTable>& SideTables() {
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}

SideTableInit這個方法在程序開始運(yùn)行的時候都調(diào)用了攻询,它初始化了StripedMap,而且SideTables()獲取到就是這個州弟,每次獲取都是最新值钧栖。

template<typename T>
class StripedMap {
    enum { CacheLineSize = 64 };
#if TARGET_OS_EMBEDDED
    enum { StripeCount = 8 };
#else
    enum { StripeCount = 64 };
#endif

    struct PaddedT {
        T value alignas(CacheLineSize);
    };
    PaddedT array[StripeCount];

    static unsigned int indexForPointer(const void *p) {
        uintptr_t addr = reinterpret_cast<uintptr_t>(p);
        return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
    }

 public:
    T& operator[] (const void *p) { 
        return array[indexForPointer(p)].value; 
    }
    const T& operator[] (const void *p) const { 
        return const_cast<StripedMap<T>>(this)[p]; 
    }
    ....

StripedMap結(jié)構(gòu)就有一個成員,PaddedT array[StripeCount]婆翔,從這可以看出這個數(shù)組有64個PaddedT結(jié)構(gòu)的變量拯杠,每個PaddedT結(jié)構(gòu)占64個字節(jié),也就是說StripedMap結(jié)構(gòu)大小就是4096個字節(jié)啃奴。PaddedT在這里就是SideTable潭陪。通過indexForPointer方法可以看到,分配SideTable的時候最蕾,是通過對象的地址的((addr >> 4) ^ (addr >> 9)) % StripeCount運(yùn)算得到的依溯,因?yàn)樽詈笫亲?4的模運(yùn)算,所以結(jié)果只能是從0-63瘟则。

using spinlock_t = mutex_tt<LOCKDEBUG>;
template <bool Debug>
class mutex_tt : nocopy_t {
    os_unfair_lock mLock;
    ...
struct SideTable {
    spinlock_t slock;//本質(zhì)就是os_unfair_lock鎖
    RefcountMap refcnts;//weak這里暫時用不上 arc會用到
    weak_table_t weak_table;//weak表

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }
    
    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }
    
    // Address-ordered lock discipline for a pair of side tables.
    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

struct weak_table_t {
    weak_entry_t *weak_entries;
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

當(dāng)StripedMap初始化成員array的時候黎炉,就初始化了64個SideTable,每個SideTable初始化都會把weak_table空間置為0醋拧。從上面可以看出來每一個弱引用都是一個weak_entry_t對象慷嗜。舉個很形象的例子,有一個宿舍樓(SideTables)丹壕,他有64個宿舍(SideTable)庆械,宿舍有很多個床位(weak_entry_t),它會根據(jù)里面人的多少來進(jìn)行適當(dāng)?shù)財(cái)U(kuò)容雀费,而且每個宿舍都有一把鎖(spinlock_t)來保護(hù)財(cái)產(chǎn)安全干奢,當(dāng)一個進(jìn)去時就鎖上,出來時解鎖盏袄。至于怎么分配宿舍忿峻,是根據(jù)人的編號來行進(jìn)的,在這里就是根據(jù)對象的地址辕羽,再根據(jù)上面的indexForPointer方法來進(jìn)行分配逛尚。

#define WEAK_INLINE_COUNT 4
#define REFERRERS_OUT_OF_LINE 2
struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            weak_referrer_t *referrers;
            uintptr_t        out_of_line_ness : 2;
            uintptr_t        num_refs : PTR_MINUS_2;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line_ness field is low bits of inline_referrers[1]
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };

    bool out_of_line() {
        return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
    }

    weak_entry_t& operator=(const weak_entry_t& other) {
        memcpy(this, &other, sizeof(other));
        return *this;
    }
    
    weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
        : referent(newReferent)
    {
        inline_referrers[0] = newReferrer;
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil;
        }
    }
};

經(jīng)過后面的一些代碼的驗(yàn)證,可以看出referent它存放的是weak要引用的對象刁愿,而inline_referrers存放的是weak對象本身绰寞,初始化為4個,如果有多于4的時候,會進(jìn)行擴(kuò)容滤钱,也就是有多個weak指向同一個對象觉壶。至于DisguisedPtr<objc_object>,你可以把它當(dāng)成id件缸。有了這些铜靶,看后面的代碼會清晰很多。

現(xiàn)在回過頭看storeWeak函數(shù)他炊,haveOld為真的時候争剿,大部分情況是weak指針指向新值了,還有一種情況是weak屬性的時候痊末。weak_unregister_no_lock函數(shù)是清理舊值的蚕苇,現(xiàn)在先看注冊新值的情況,是這個weak_register_no_lock函數(shù)凿叠。

id  weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // 保證引用對象是否有效
    bool deallocating;
    // 是否有自定義的默認(rèn)方法涩笤,如retain/release
    if (!referent->ISA()->hasCustomRR()) {
    //是否釋放
        deallocating = referent->rootIsDeallocating();
    }
    else {
        BOOL (*allowsWeakReference)(objc_object *, SEL) = 
            (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_allowsWeakReference);
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            return nil;
        }
        deallocating =
            ! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
    }

    if (deallocating) {
        if (crashIfDeallocating) {
            _objc_fatal("Cannot form weak reference to instance (%p) of "
                        "class %s. It is possible that this object was "
                        "over-released, or is in the process of deallocation.",
                        (void*)referent, object_getClassName((id)referent));
        } else {
            return nil;
        }
    }

    // now remember it and where it is being stored
    weak_entry_t *entry;
    //獲取weak引用對象
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        //如果存在就把weak對象 加進(jìn)去
        append_referrer(entry, referrer);
    } 
    else {
        //創(chuàng)建weak弱引用表關(guān)聯(lián)
        weak_entry_t new_entry(referent, referrer);
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
    }

    // Do not set *referrer. objc_storeWeak() requires that the 
    // value not change.
    return referent_id;
}

先一行一行分析創(chuàng)建weak弱引用:

weak_entry_t new_entry(referent, referrer);
//對應(yīng)于
weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
        : referent(newReferent)
    {
        inline_referrers[0] = newReferrer;
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil;
        }
    }

從這就能看出referent對應(yīng)的是weak要引用的對象,inline_referrers對應(yīng)的是weak本身幔嫂。

static void weak_grow_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);

    // Grow if at least 3/4 full.
    if (weak_table->num_entries >= old_size * 3 / 4) {
        weak_resize(weak_table, old_size ? old_size*2 : 64);
    }
}

static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
    size_t old_size = TABLE_SIZE(weak_table);

    weak_entry_t *old_entries = weak_table->weak_entries;
    weak_entry_t *new_entries = (weak_entry_t *)
        calloc(new_size, sizeof(weak_entry_t));

    weak_table->mask = new_size - 1;
    weak_table->weak_entries = new_entries;
    weak_table->max_hash_displacement = 0;
    weak_table->num_entries = 0;  // restored by weak_entry_insert below
    
    if (old_entries) {
        weak_entry_t *entry;
        weak_entry_t *end = old_entries + old_size;
        for (entry = old_entries; entry < end; entry++) {
            if (entry->referent) {
                weak_entry_insert(weak_table, entry);
            }
        }
        free(old_entries);
    }
}

從一開始old_size ? old_size*2 : 64為0辆它,所以開辟出了64個大小為sizeof(weak_entry_t)空間,如果里面的個數(shù)超過64的3/4了履恩,就開始再次擴(kuò)容锰茉,并把之前的表結(jié)構(gòu)再一個一個插入進(jìn)去,weak_entry_insert(weak_table, entry);切心。

static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
    weak_entry_t *weak_entries = weak_table->weak_entries;
    assert(weak_entries != nil);

    size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (weak_entries[index].referent != nil) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_entries);
        hash_displacement++;
    }

    weak_entries[index] = *new_entry;
    weak_table->num_entries++;

    if (hash_displacement > weak_table->max_hash_displacement) {
        weak_table->max_hash_displacement = hash_displacement;
    }
}

hash_pointer是根據(jù)對象的地址做一次hash運(yùn)算飒筑,用的是ptr_hash方法,再跟總共的空間做與運(yùn)行绽昏,也就是跟63做與運(yùn)算(如果再次擴(kuò)容就不是63了)协屡。如果當(dāng)前位置有值了,就再通過上面算法再換一個全谤,接下來就是簡單賦值了肤晓。

static weak_entry_t * weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
    assert(referent);
    weak_entry_t *weak_entries = weak_table->weak_entries;
    if (!weak_entries) return nil;

    size_t begin = hash_pointer(referent) & weak_table->mask;
    size_t index = begin;
    size_t hash_displacement = 0;
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_table->weak_entries);
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }
    
    return &weak_table->weak_entries[index];
}

通過一個對象獲取對應(yīng)的weak_entry_t,這里跟插入的差不多认然,都是根據(jù)地址來進(jìn)行操作补憾。

static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
    if (! entry->out_of_line()) {
        // Try to insert inline.
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == nil) {
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }

        // Couldn't insert inline. Allocate out of line.
        weak_referrer_t *new_referrers = (weak_referrer_t *)
            calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
        // This constructed table is invalid, but grow_refs_and_insert
        // will fix it and rehash it.
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            new_referrers[i] = entry->inline_referrers[I];
        }
        entry->referrers = new_referrers;
        entry->num_refs = WEAK_INLINE_COUNT;
        entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
        entry->mask = WEAK_INLINE_COUNT-1;
        entry->max_hash_displacement = 0;
    }

    assert(entry->out_of_line());

    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        return grow_refs_and_insert(entry, new_referrer);
    }
    size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (entry->referrers[index] != nil) {
        hash_displacement++;
        index = (index+1) & entry->mask;
        if (index == begin) bad_weak_table(entry);
    }
    if (hash_displacement > entry->max_hash_displacement) {
        entry->max_hash_displacement = hash_displacement;
    }
    weak_referrer_t &ref = entry->referrers[index];
    ref = new_referrer;
    entry->num_refs++;
}

從上面可知,有2種模式卷员,如果有小于等于4個weak的話盈匾,就用inline_referrers本身,就如最開始for循環(huán)毕骡,如果大于4個的話削饵,就跟weak表結(jié)構(gòu)一樣了岩瘦,用referrers進(jìn)行存儲了,如果里面?zhèn)€數(shù)大于3/4就自動擴(kuò)容一倍窿撬,它用的是w_hash_pointer方法對地址進(jìn)行hash取值启昧,其實(shí)跟hash_pointer一樣,調(diào)用的都是同一個方法ptr_hash劈伴。

void weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;
    if (!referent) return;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        remove_referrer(entry, referrer);
        bool empty = true;
        if (entry->out_of_line()  &&  entry->num_refs != 0) {
            empty = false;
        }
        else {
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i]) {
                    empty = false; 
                    break;
                }
            }
        }

        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }
}

static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
    if (! entry->out_of_line()) {
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == old_referrer) {
                entry->inline_referrers[i] = nil;
                return;
            }
        }
        ...
    }

    size_t begin = w_hash_pointer(old_referrer) & (entry->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    ....
    entry->referrers[index] = nil;
    entry->num_refs--;
}

移除weak引用箫津,主要就是把相應(yīng)位置的指針置為空,entry->inline_referrers[i] = nil;宰啦、entry->referrers[index] = nil;。如果沒有引用了饼拍,就直接調(diào)用weak_entry_remove方法把weak表里面的相應(yīng)引用給置為空赡模。

static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
    // remove entry
    if (entry->out_of_line()) free(entry->referrers);
    bzero(entry, sizeof(*entry));
    weak_table->num_entries--;
    weak_compact_maybe(weak_table);
}

當(dāng)一個對象銷毀的時候,如果有弱引用师抄,會調(diào)用weak_clear_no_lock方法進(jìn)行清除漓柑,最后也會調(diào)用weak_entry_remove

inline void  objc_object::clearDeallocating()
{
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // Slow path for non-pointer isa with weak refs and/or side table data.
        clearDeallocating_slow();
    }
}

objc_object::clearDeallocating_slow()
{
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
    table.unlock();
}

void weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    objc_object *referent = (objc_object *)referent_id;
    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
    if (entry == nil) return;
    
    weak_referrer_t *referrers;
    size_t count;
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    }else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[I];
        if (referrer) {
            if (*referrer == referent) {
                *referrer = nil;
            }
            ...
        }
    }
    weak_entry_remove(weak_table, entry);
}

這里的邏輯跟之前相似叨吮,看上面的即可辆布。weak的介紹到此為止,詳細(xì)的可以自己運(yùn)行別人編譯好的runtime自己進(jìn)行調(diào)式茶鉴。

結(jié)構(gòu)圖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锋玲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子涵叮,更是在濱河造成了極大的恐慌惭蹂,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件割粮,死亡現(xiàn)場離奇詭異盾碗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)舀瓢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門廷雅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人京髓,你說我怎么就攤上這事航缀。” “怎么了朵锣?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵谬盐,是天一觀的道長。 經(jīng)常有香客問我诚些,道長飞傀,這世上最難降的妖魔是什么皇型? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮砸烦,結(jié)果婚禮上弃鸦,老公的妹妹穿的比我還像新娘。我一直安慰自己幢痘,他們只是感情好唬格,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颜说,像睡著了一般购岗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上门粪,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天喊积,我揣著相機(jī)與錄音,去河邊找鬼玄妈。 笑死乾吻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拟蜻。 我是一名探鬼主播绎签,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼酝锅!你這毒婦竟也來了诡必?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤屈张,失蹤者是張志新(化名)和其女友劉穎擒权,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阁谆,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碳抄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了场绿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剖效。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖焰盗,靈堂內(nèi)的尸體忽然破棺而出璧尸,到底是詐尸還是另有隱情,我是刑警寧澤熬拒,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布爷光,位于F島的核電站,受9級特大地震影響澎粟,放射性物質(zhì)發(fā)生泄漏蛀序。R本人自食惡果不足惜欢瞪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望徐裸。 院中可真熱鬧遣鼓,春花似錦、人聲如沸重贺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽气笙。三九已至次企,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間潜圃,已是汗流浹背抒巢。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秉犹,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓稚晚,卻偏偏與公主長得像崇堵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子客燕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

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