iOS autoreleasepool 原理解析

一、介紹

autoreleasepool 自動(dòng)釋放池梢杭,在池子里的對(duì)象如果沒有被強(qiáng)引用都會(huì)自動(dòng)釋放掉温兼,自動(dòng)釋放池的主要底層數(shù)據(jù)結(jié)構(gòu)是:__AtAutoreleasePoolAutoreleasePoolPage武契,調(diào)用了autorelease的對(duì)象最終都是通過 AutoreleasePoolPage 對(duì)象來管理的募判。

二缸榛、源碼分析 - clang重寫 @autoreleasepool

先關(guān)閉ARC,Build Setting 搜索 Objective-C Automatic Reference Counting Yes 改為 No兰伤,使用 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp 命令生成C++代碼内颗。
main.m代碼如下:

    #import <Foundation/Foundation.h>
    #import "Person.h"

    int main(int argc, char * argv[]) {
        @autoreleasepool {
            Person *person = [[[Person alloc]init]autorelease];
        }
        return 0;
    }

main.cpp對(duì)應(yīng)代碼如下:

int main(int argc, char * argv[]) {
/* @autoreleasepool */ 
    { 
        __AtAutoreleasePool __autoreleasepool; 
        Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease"));
    }
    return 0;
}

__AtAutoreleasePool 結(jié)構(gòu)體對(duì)應(yīng)的代碼如下:

/// struct 有點(diǎn)類似 Class
struct __AtAutoreleasePool {
    /// 構(gòu)造函數(shù) 創(chuàng)建對(duì)象
    __AtAutoreleasePool() {
        atautoreleasepoolobj = objc_autoreleasePoolPush();
    }
    /// 析構(gòu)函數(shù) 銷毀對(duì)象
    ~__AtAutoreleasePool() {
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    /// 聲明的對(duì)象(接收構(gòu)造函數(shù)生成的對(duì)象,也是傳入析構(gòu)函數(shù)的)
    void * atautoreleasepoolobj;
};

一個(gè)@autoreleasepool的 {} 相當(dāng)于創(chuàng)建和銷毀敦腔,整個(gè)作用域就在大括號(hào)內(nèi)均澳,那么其實(shí)開始的main.m里面的代碼相當(dāng)于如下代碼:

int main(int argc, char * argv[]) {
    atautoreleasepoolobj = objc_autoreleasePoolPush();

    Person *person = [[[Person alloc]init]autorelease];

    objc_autoreleasePoolPop(atautoreleasepoolobj);
    return 0;
}
三、源碼分析 AutoreleasePoolPage類對(duì)象

想要知道objc_autoreleasePoolPush和Pop做了什么符衔,那就得去看看objc4源碼里面具體做了啥找前,可以看到Push是調(diào)用了 AutoreleasePoolPage 類的 push() 方法,反之就是 pop()方法判族,所以自動(dòng)釋放池就是 AutoreleasePoolPage 對(duì)象來管理的躺盛。

void *objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

void objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

AutoreleasePoolPage 類大概有600多行代碼,截取部分代碼展示如下:

class AutoreleasePoolPage
{
    magic_t const magic;
    /// 指向下一個(gè)可放對(duì)象的地址
    id *next;
    /// 在固定的一個(gè)線程執(zhí)行形帮,線程一一對(duì)應(yīng)槽惫,線程之間的釋放池是分開的
    pthread_t const thread;
    /// 鏈表父指針,指向上一個(gè)Page對(duì)象
    AutoreleasePoolPage * const parent;
    /// 鏈表子指針辩撑,指向下一個(gè)Page對(duì)象
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t hiwat;

    AutoreleasePoolPage(AutoreleasePoolPage *newParent)
    : magic(), next(begin()), thread(pthread_self()),
      parent(newParent), child(nil),
      depth(parent ? 1+parent->depth : 0),
      hiwat(parent ? parent->hiwat : 0) {
        if (parent) {
            parent->check();
            assert(!parent->child);
            parent->unprotect();
            parent->child = this;
            parent->protect();
        }
        protect();
    }
    id * begin() {
        return (id *) ((uint8_t *)this+sizeof(*this));
    }
    id * end() {
        /// 以前這個(gè)SIZE = PAGE_MAX_SIZE = PAGE_SIZE = 4096
        /// 現(xiàn)在好像是SIZE = PAGE_MAX_SIZE = 1 << 14界斜,就是二進(jìn)制的100000000000000 十進(jìn)制的1乘以2的14次方,即2的14次方 = 16384合冀,還是說我下載的[obj4](https://opensource.apple.com/tarballs/objc4/)代碼版本不對(duì)各薇,望知道的大佬告訴我一下
        return (id *) ((uint8_t *)this+SIZE);
    }
    ~AutoreleasePoolPage()
    {
        check();
        unprotect();
        assert(empty());
        assert(!child);
    }
    id *add(id obj)
    {
        assert(!full());
        unprotect();
        id *ret = next;  // faster than `return next-1` because of aliasing
        *next++ = obj;
        protect();
        return ret;
    }

    void releaseAll() 
    {
        releaseUntil(begin());
    }

    void releaseUntil(id *stop) 
    {
        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
    
        while (this->next != stop) {
            // Restart from hotPage() every time, in case -release 
            // autoreleased more objects
            AutoreleasePoolPage *page = hotPage();

            // fixme I think this `while` can be `if`, but I can't prove it
            while (page->empty()) {
                page = page->parent;
                setHotPage(page);
            }

            page->unprotect();
            id obj = *--page->next;
            memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
            page->protect();

            if (obj != POOL_BOUNDARY) {
                objc_release(obj);
            }
        }

        setHotPage(this);

#if DEBUG
        // we expect any children to be completely empty
        for (AutoreleasePoolPage *page = child; page; page = page->child) {
             assert(page->empty());
        }
#endif
    }
    void kill()
    {
        AutoreleasePoolPage *page = this;
        while (page->child) page = page->child;
        AutoreleasePoolPage *deathptr;
        do {
            deathptr = page;
            page = page->parent;
            if (page) {
                page->unprotect();
                page->child = nil;
                page->protect();
            }
            delete deathptr;
        } while (deathptr != this);
    }
public:
    static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        id *dest __unused = autoreleaseFast(obj);
        assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
        return obj;
    }
    /// 方法實(shí)現(xiàn)省略
    static inline void *push() {}
    static inline void pop(void *token) {}
    static void init() {}
    void print() {}
    static void printAll() {}
    static void printHiwat() 
};

上面有個(gè) end() 方法可以知道每個(gè) AutoreleasePoolPage 對(duì)象的大小,以前這個(gè)SIZE = PAGE_MAX_SIZE = PAGE_SIZE = 4096君躺,現(xiàn)在好像是SIZE = PAGE_MAX_SIZE = 1 << 14峭判,就是二進(jìn)制的100000000000000 十進(jìn)制的1乘以2的14次方,即2的14次方 = 16384棕叫,還是說我下載的obj4代碼版本不對(duì)林螃,望知道的大佬告訴我一下,以下就先按照4096來講谍珊。

每個(gè) AutoreleasePoolPage 對(duì)象占用4096字節(jié)內(nèi)存治宣,除了用來存放它內(nèi)部的成員變量急侥,剩下的空間用來存放 autorelease 對(duì)象(調(diào)用了autorelease方法的對(duì)象砌滞,如上面的person對(duì)象)的地址所有的 AutoreleasePoolPage 對(duì)象通過雙向鏈表的形式連接在一起,第一個(gè)對(duì)象的parent指向的是空坏怪,child指向下一個(gè)對(duì)象贝润,下一個(gè)對(duì)象的parent指向上一個(gè)對(duì)象,child指向下一個(gè)對(duì)象铝宵,這樣循環(huán)最后一個(gè)對(duì)象的child指向是空打掘,這就是雙向鏈表結(jié)構(gòu)华畏。

image.png

假設(shè)第一個(gè) AutoreleasePoolPage 對(duì)象的起始地址是 0x1000,那么結(jié)束地址就是 0x2000尊蚁,16進(jìn)制換算一下 0x1000 = 4096亡笑,自己所帶的7個(gè)成員變量占56個(gè)字節(jié),所以能夠存放 autorelease 對(duì)象的地址就是0x1038~0x2000横朋,也可以通過 begin()方法算出開始地址是多少仑乌。

四、 objc_autoreleasePoolPush琴锭,objc_autoreleasePoolPop晰甚,autorelease 方法做了什么
  • AutoreleasePoolPage 類對(duì)象中*next 指向了下一個(gè)能存放 autorelease 對(duì)象地址的區(qū)域 。

  • 調(diào)用 objc_autoreleasePoolPush 方法會(huì)將一個(gè) POOL_BOUNDARY 入棧(數(shù)據(jù)結(jié)構(gòu)算是棧結(jié)構(gòu))决帖,并且返回其存放的內(nèi)存地址厕九。

  • 調(diào)用 objc_autoreleasePoolPop 方法時(shí)傳入一個(gè) POOL_BOUNDARY 的內(nèi)存地址,會(huì)從最后一個(gè)入棧的對(duì)象調(diào)用對(duì)象的 release 方法地回,直到遇到這個(gè) POOL_BOUNDARY扁远,可以在上面的部分源碼 releaseAll()方法里面查看到。

  • 對(duì)象的 autorelease 方法 通過斷點(diǎn)看匯編代碼發(fā)現(xiàn)方法實(shí)際是調(diào)用了 objc_autorelease() 方法刻像,把對(duì)象添加到Page對(duì)象中去穿香,通過objc4源碼可以看到調(diào)用順序如下。

1绎速、obj->autorelease()
2皮获、rootAutorelease()
3、rootAutorelease2()
4纹冤、AutoreleasePoolPage類的autorelease()
5洒宝、autoreleaseFast()
static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {
            return page->add(obj);
        } else if (page) {
            return autoreleaseFullPage(obj, page);
        } else {
            return autoreleaseNoPage(obj);
        }
    }

objc_autoreleasePoolPush,objc_autoreleasePoolPop

int main(int argc, char * argv[]) {
    @autoreleasepool {
        for (int i = 0; i<1000; i++) {
            Person *person = [[[Person alloc]init]autorelease];
        }
    }
    /**上面代碼 翻譯過來基本就是下面這樣*/
    /// 將 POOL_BOUNDARY 入棧 返回地址其實(shí)就是 0x1038 因?yàn)榈谝粋€(gè)可存放對(duì)象的地址就是它
    atautoreleasepoolobj = objc_autoreleasePoolPush();
    // atautoreleasepoolobj = 0x1038;

    /// 循環(huán)將 person 對(duì)象放入到 AutoreleasePoolPage 對(duì)象去萌京,不夠就新建一個(gè)Page對(duì)象child指針指向
    for (int i = 0; i<1000; i++) {
        Person *person = [[[Person alloc]init]autorelease];
    }
    /// 將 0x1038 傳入雁歌,從棧頂開始釋放對(duì)象,一直釋放到 0x1038 對(duì)象為止
    objc_autoreleasePoolPop(atautoreleasepoolobj);
    // objc_autoreleasePoolPop(0x1038);
    return 0;
}

如果是 autoreleasepool 嵌套知残,其實(shí)也是一樣的靠瞎,沒有用完一個(gè)page對(duì)象的空間不會(huì)再生成一個(gè)新的page對(duì)象,而是在原對(duì)象中繼續(xù)壓入POOL_BOUNDARY來區(qū)分求妹。

int main(int argc, char * argv[]) {
    /// 創(chuàng)建一個(gè) AutoreleasePoolPage 對(duì)象
    @autoreleasepool { /// pool_boundary_1 = objc_autoreleasePoolPush(), POOL_BOUNDARY 壓進(jìn)去
        /// person1 對(duì)象 壓入
        Person *person1 = [[[Person alloc]init]autorelease];
        /// person2 對(duì)象 壓入
        Person *person2 = [[[Person alloc]init]autorelease];
    
        /// 沒有超出 4096 字節(jié)乏盐,不用再創(chuàng)建 AutoreleasePoolPage 對(duì)象
        @autoreleasepool { /// pool_boundary_2 = objc_autoreleasePoolPush(), POOL_BOUNDARY 再壓進(jìn)去
            /// person3 對(duì)象 壓入
            Person *person3 = [[[Person alloc]init]autorelease];
            /// person4 對(duì)象 壓入
            Person *person4 = [[[Person alloc]init]autorelease];
        } /// objc_autoreleasePoolPop(pool_boundary_2), 從棧頂開始釋放到 pool_boundary_2 為止 

        /// person5 對(duì)象 壓入
        Person *person5 = [[[Person alloc]init]autorelease];
        /// person6 對(duì)象 壓入
        Person *person6 = [[[Person alloc]init]autorelease];
    } /// objc_autoreleasePoolPop(pool_boundary_1), 從棧頂開始釋放到 pool_boundary_1 為止
    return 0;
}
五、通過 _objc_autoreleasePoolPrint 方法查看自動(dòng)釋放池的具體情況

objc4 源碼的 NSObject.mm 文件中有一個(gè) _objc_autoreleasePoolPrint 方法制恍,實(shí)際調(diào)用了 AutoreleasePoolPage的打印輸出方法父能,我們來看看能看到哪些信息。

void _objc_autoreleasePoolPrint(void)
{
    AutoreleasePoolPage::printAll();
}

例子代碼如下

/// 需要先這樣定義一下方法名净神,這樣才能調(diào)用何吝,runtime才會(huì)去Foundation框架去找實(shí)現(xiàn)的方法
extern void _objc_autoreleasePoolPrint(void);

int main(int argc, char * argv[]) {
    @autoreleasepool {
        Person *person1 = [[[Person alloc]init]autorelease];
        Person *person2 = [[[Person alloc]init]autorelease];
    
        _objc_autoreleasePoolPrint();
    
        @autoreleasepool {
            Person *person3 = [[[Person alloc]init]autorelease];
            Person *person4 = [[[Person alloc]init]autorelease];
        }

    }
    return 0;
}

打印如下溉委,

  • 3 release pending 是因?yàn)榈谝粋€(gè)是Push壓入的 POOL_BOUNDARY 所以 POOL 0x7fe4e380e038 代表的就是,然后才是person1爱榕,person2對(duì)象瓣喊。
  • PAGE (hot) (cold) 代表的就是一個(gè) AutoreleasePoolPage 對(duì)象,(hot) 代表的是當(dāng)前正在使用的
objc[10164]: ##############
objc[10164]: AUTORELEASE POOLS for thread 0x103d34600
objc[10164]: 3 releases pending.
objc[10164]: [0x7fe4e380e000]  ................  PAGE  (hot) (cold)
objc[10164]: [0x7fe4e380e038]  ################  POOL 0x7fe4e380e038
objc[10164]: [0x7fe4e380e040]    0x6000020440c0  Person
objc[10164]: [0x7fe4e380e048]    0x6000020440d0  Person
objc[10164]: ##############

再換一個(gè)例子

/// 需要先這樣定義一下方法名黔酥,這樣才能調(diào)用型宝,runtime才會(huì)去Foundation框架去找實(shí)現(xiàn)的方法
extern void _objc_autoreleasePoolPrint(void);

int main(int argc, char * argv[]) {
    @autoreleasepool {
        Person *person1 = [[[Person alloc]init]autorelease];
        Person *person2 = [[[Person alloc]init]autorelease];
    
        @autoreleasepool {
            for (int i = 0; i<1000; i++) {
                Person *person3 = [[[Person alloc]init]autorelease];
            }
        
            _objc_autoreleasePoolPrint();
        }
    }
    return 0;
}

打印如下,第一個(gè) PAGE 多了一個(gè) (full)絮爷,代表第一個(gè)對(duì)象滿了趴酣,沒有了 (hot) 代表不是當(dāng)前使用頁,當(dāng)前使用頁切到下一個(gè) PAGE 對(duì)象上了坑夯。

objc[10354]: ##############
objc[10354]: AUTORELEASE POOLS for thread 0x110cf5600
objc[10354]: 1004 releases pending.
objc[10354]: [0x7fd8fb009000]  ................  PAGE (full)  (cold)
objc[10354]: [0x7fd8fb009038]  ################  POOL 0x7fd8fb009038
objc[10354]: [0x7fd8fb009040]    0x600000f1c020  Person
objc[10354]: [0x7fd8fb009048]    0x600000f1c030  Person
objc[10354]: [0x7fd8fb009050]  ################  POOL 0x7fd8fb009050
objc[10354]: [0x7fd8fb009058]    0x600000f1c040  Person
......
......
......
objc[10354]: [0x7fd8fb00b000]  ................  PAGE  (hot)
......
......
......
objc[10354]: [0x7fd8fb00bfc8]    0x600000f1feb0  Person
objc[10354]: ##############
六岖寞、Runloop與Autorelease的關(guān)系(對(duì)象到底在什么時(shí)候調(diào)用對(duì)象的release方法)

先來看一段代碼,MRC演示環(huán)境柜蜈。

image.png
根據(jù)上面的源碼跟讀我們可以知道仗谆,第一個(gè) person 對(duì)象在 autoreleasepool的大括號(hào)結(jié)束的時(shí)候會(huì)調(diào)用對(duì)象的 release 方法。一眼看代碼淑履,其實(shí)感覺第二個(gè)person對(duì)象可能是加入到 main 函數(shù)里面的 autoreleasepool 里面去了隶垮,如果是那么理論上就是App不結(jié)束都不會(huì)釋放,如果創(chuàng)建巨多的對(duì)象秘噪,那這樣顯然不合理狸吞,所以我們看到其實(shí)第二個(gè) person 對(duì)象也釋放了,那么第二個(gè)對(duì)象是在viewDidload大括號(hào)結(jié)束后就立即調(diào)用release方法引用計(jì)數(shù)-1的么指煎?
image.png
按照上圖的例子來看好像并不是這樣蹋偏,先執(zhí)行的 viewwillAppear 方法,那么具體原因是為何呢至壤,其實(shí)和 RunLoop 有關(guān)威始,iOS在主線程的Runloop中注冊(cè)了2個(gè)跟 Autoreleasepool 相關(guān)的 Observer

1像街、第1個(gè) Observer 監(jiān)聽了 kCFRunLoopEntry 事件黎棠,會(huì)調(diào)用 objc_autoreleasePoolPush()

2、第2個(gè) Observer 監(jiān)聽了 kCFRunLoopBeforeWaiting 事件镰绎,會(huì)調(diào)用objc_autoreleasePoolPop()脓斩、objc_autoreleasePoolPush() ,監(jiān)聽了kCFRunLoopBeforeExit事件跟狱,會(huì)調(diào)用objc_autoreleasePoolPop()

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0), /// 1
    kCFRunLoopBeforeTimers = (1UL << 1), /// 2
    kCFRunLoopBeforeSources = (1UL << 2),/// 4
    kCFRunLoopBeforeWaiting = (1UL << 5),/// 32
    kCFRunLoopAfterWaiting = (1UL << 6),/// 64 
    kCFRunLoopExit = (1UL << 7),/// 128
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};
image.png

以前打印當(dāng)前RunLoop是可以看到注冊(cè)的2個(gè)Observer的俭厚,兩個(gè) callout = _wrapRunLoopWithAutoreleasePoolHandleractivities = 0x1 = 1 = kCFRunLoopEntryactivities = 0xa0 = 160 = kCFRunLoopBeforeWaiting | kCFRunLoopExit驶臊,但是現(xiàn)在打印看不到了挪挤,知道原因的大佬可以留言告訴我一下,謝謝关翎!

結(jié)論:

  • MRC時(shí)候?qū)ο笳{(diào)用 autorelease 方法加入到自動(dòng)釋放池后扛门,release 的時(shí)機(jī)是在所在的一次 RunLoop 結(jié)束的時(shí)候釋放。
  • ARC 時(shí)候由系統(tǒng)管理纵寝,系統(tǒng)沒有調(diào)用 autorelease 方法论寨,而是在合適的地方(離開對(duì)象作用域之前)底層調(diào)用了 person = nil 方法。
    image.png
    image.png

    image.png

從匯編代碼看確實(shí)相當(dāng)于作用域離開前調(diào)用了 person = nil; 也就是 objc_storeStrong(&person,nil)

void objc_storeStrong(id *location, id obj)
{
    id prev = *location;
    if (obj == prev) {
        return;
    }
    objc_retain(obj);
    *location = obj;
    objc_release(prev);
}
七爽茴、擴(kuò)展 Tagged Pointer
static inline id autorelease(id obj)
{
    assert(obj);
    assert(!obj->isTaggedPointer());
    id *dest __unused = autoreleaseFast(obj);
    assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
    return obj;
}

有沒有發(fā)現(xiàn) obj->isTaggedPointer() 這里斷言了葬凳,如果是Tagged Pointer類型對(duì)象就不加入到自動(dòng)釋放池里去,因?yàn)檫@種類型數(shù)據(jù)是存在指針里面的室奏,指針本身也是有內(nèi)存地址的火焰,本身也能存儲(chǔ)少量的數(shù)據(jù)。Tagged Pointer 詳解

如有錯(cuò)誤請(qǐng)幫忙指出胧沫,謝謝昌简!轉(zhuǎn)載請(qǐng)注明出處,喜歡的話绒怨,請(qǐng)點(diǎn)個(gè)贊吧纯赎!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市南蹂,隨后出現(xiàn)的幾起案子犬金,更是在濱河造成了極大的恐慌,老刑警劉巖六剥,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佑附,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡仗考,警方通過查閱死者的電腦和手機(jī)音同,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秃嗜,“玉大人权均,你說我怎么就攤上這事」牵” “怎么了叽赊?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長必搞。 經(jīng)常有香客問我必指,道長,這世上最難降的妖魔是什么恕洲? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任塔橡,我火速辦了婚禮梅割,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘葛家。我一直安慰自己户辞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布癞谒。 她就那樣靜靜地躺著底燎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弹砚。 梳的紋絲不亂的頭發(fā)上双仍,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音桌吃,去河邊找鬼朱沃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛读存,可吹牛的內(nèi)容都是我干的为流。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼让簿,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼敬察!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尔当,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤莲祸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后椭迎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锐帜,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年畜号,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缴阎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡简软,死狀恐怖蛮拔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情痹升,我是刑警寧澤建炫,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站疼蛾,受9級(jí)特大地震影響肛跌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一衍慎、第九天 我趴在偏房一處隱蔽的房頂上張望转唉。 院中可真熱鬧,春花似錦西饵、人聲如沸酝掩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至原朝,卻和暖如春驯嘱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喳坠。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工鞠评, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壕鹉。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓剃幌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親晾浴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子负乡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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