iOS-RunLoop2-線程保活

如果經(jīng)常要在子線程中做事情或链,不使用北鼓眨活,就會一直創(chuàng)建澳盐、銷毀子線程祈纯,這樣很耗性能的,所以經(jīng)常在子線程做事情最好使用線程钡鸢遥活腕窥,比如AFN2.X就使用RunLoop實現(xiàn)了線程保活筛婉。

一. 實現(xiàn)線程贝乇活

為了監(jiān)控線程生命周期我們自定義MJThread繼承于NSThread,重寫其dealloc方法爽撒,實現(xiàn)如下代碼:

#import "ViewController.h"
#import "MJThread.h"

@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //self和thread會造成循環(huán)引用
    self.thread = [[MJThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    [self.thread start];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
    //waitUntilDone:YES 等到子線程任務執(zhí)行完再執(zhí)行下面NSLog
    //NO 不用等到子線程執(zhí)行完再執(zhí)行下面NSLog(下面NSLog在主線程入蛆,test在子線程,同時執(zhí)行)
    NSLog(@"123");
}

// 子線程需要執(zhí)行的任務
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    // 沒打印dealloc硕勿,也沒打印----end----
    // -[ViewController test] <MJThread: 0x600000a8fec0>{number = 3, name = (null)}
}

// 這個方法的目的:線程鄙诨伲活
- (void)run {
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    
    // 往RunLoop里面添加Source\Timer\Observer,Port相關的是Source1事件
    [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
    
    //添加了一個Source1源武,但是這個Source1也沒啥事扼褪,所以線程在這里就休眠了想幻,不會往下走,不會打印----end----
    //如果不添加Source\Timer\Observer话浇,RunLoop沒有任何事件處理RunLoop就會立馬退出脏毯,打印----end----
    [[NSRunLoop currentRunLoop] run];
    
    NSLog(@"%s ----end----", __func__);
}

@end

上面代碼,如果只寫 [[NSRunLoop currentRunLoop] run]凳枝,不添加Port抄沮,RunLoop沒有任何事件處理,那么RunLoop就會立馬退出岖瑰,會打印----end----叛买,如果添加Port,并且[[NSRunLoop currentRunLoop] run]蹋订,如下:

[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];

這樣NSRunLoop里面有事件(雖然不用處理什么)率挣,就不會退出了。線程在[[NSRunLoop currentRunLoop] run]這一行就休眠了露戒,不會往下執(zhí)行打印----end----了椒功,如果有其他事情,線程會再次被喚醒智什,處理事情动漾。

上面的代碼有兩個問題:

  1. self和thread會造成循環(huán)引用,都不會釋放
  2. thread一直不會死

首先解決循環(huán)引用:

#import "ViewController.h"
#import "MJThread.h"

@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
        //如果使用如下方式創(chuàng)建thread荠锭,self會引用thread旱眯,thread會引用self,會造成循環(huán)引用证九。
        //[[MJThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    
        self.thread = [[MJThread alloc] initWithBlock:^{
        NSLog(@"%@----begin----", [NSThread currentThread]);
        
        // 往RunLoop里面添加Source\Timer\Observer
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        
        //線程會一直阻塞這這一行删豺,永遠不會銷毀
        [[NSRunLoop currentRunLoop] run]; 

        //當把NSRunLoop停掉之后,代碼就會從下一行往下走愧怜,這時候任務執(zhí)行完成呀页,線程該死的時候就會死了。
        NSLog(@"%@----end----", [NSThread currentThread]);
    }];
    [self.thread start];
}

- (void)dealloc
{
    NSLog(@"%s", __func__);

    //就算把thread清空拥坛,thread也不會銷毀蓬蝶,因為任務還沒結束,線程就不會死渴逻。
    //self.thread = nil; 
}

運行后疾党,在當前界面返回,打硬肄取:

-[ViewController dealloc]

可以發(fā)現(xiàn)ViewController銷毀了,但是thread還是沒被銷毀竭钝,為什么呢梨撞?

這是因為RunLoop在 [[NSRunLoop currentRunLoop] run]這一行一直阻塞雹洗,一直不會打印----end----,這時候任務一直在進行卧波,任務還沒有完成線程就不會死时肿,就算在ViewController的dealloc方法里面把thread清空,thread也不會死港粱。

那怎么解決線程不會死的問題呢螃成?
線程不會死的原因就是有個RunLoop一直在運行,線程一直有任務做查坪,所以想讓線程死掉寸宏,就把RunLoop停掉,當把RunLoop停掉之后偿曙,代碼就會從 [[NSRunLoop currentRunLoop] run]往下走氮凝,當線程執(zhí)行完任務后,線程該死的時候(當前控制器銷毀后)就會死了望忆。

我們看run方法的解釋:

it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:.
In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.

翻譯過來就是:
它通過反復調用runMode:beforeDate:在NSDefaultRunLoopMode中運行接收器罩阵。換句話說,這個方法有效地開始了一個無限循環(huán)启摄,處理來自運行循環(huán)的輸入源和計時器的數(shù)據(jù)稿壁。

可以看出,通過run方法運行的RunLoop是無法停止的歉备,它專門用于開啟一個永不銷毀的線程(NSRunLoop)傅是。

既然這樣,那我們可以模仿run方法威创,寫個while循環(huán)落午,內部也調用runMode:beforeDate:方法,如下:

while (!weakSelf.isStoped) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
//while的條件判斷中要使用weakSelf肚豺,不然self強引用thread溃斋,thread強引用block,block強引用self吸申,產(chǎn)生循環(huán)引用

不使用run方法梗劫,我們就能停掉RunLoop了,停掉RunLoop系統(tǒng)有提供API是CFRunLoopStop(CFRunLoopGetCurrent())截碴,但是這個API不能在ViewController的dealloc方法里面寫梳侨,因為ViewController的dealloc方法是在主線程調用的,我們要保證在子線程調用CFRunLoopStop(CFRunLoopGetCurrent())日丹。

最終代碼如下:

#import "ViewController.h"
#import "MJThread.h"

@interface ViewController ()

@property (strong, nonatomic) MJThread *thread;
@property (assign, nonatomic, getter=isStoped) BOOL stopped;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    __weak typeof(self) weakSelf = self;

    self.stopped = NO;
    
    //如果使用如下方式創(chuàng)建thread走哺,self會引用thread,thread會引用self哲虾,會造成循環(huán)引用丙躏。
    //[[MJThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    
    self.thread = [[MJThread alloc] initWithBlock:^{
        NSLog(@"%@----begin----", [NSThread currentThread]);
        
        // 往RunLoop里面添加Source\Timer\Observer
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        //要使用weakself择示,不然self強引用thread,thread強引用block晒旅,block強引用self栅盲,產(chǎn)生循環(huán)引用。
        while (!weakSelf.isStoped) {
            //beforeDat:過期時間废恋,傳入distantFuture遙遠的未來谈秫,就是永遠不會過期
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        
        NSLog(@"%@----end----", [NSThread currentThread]);
        
        // NSRunLoop的run方法是無法停止的,它專門用于開啟一個永不銷毀的線程(NSRunLoop)
        // [[NSRunLoop currentRunLoop] run];
        /*
         it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:.
         In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers
         */
    }];
    [self.thread start];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}

// 子線程需要執(zhí)行的任務
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}

//點擊停止按鈕
- (IBAction)stop {
    // 在子線程調用CFRunLoopStop
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:NO];
}

// 停止子線程的RunLoop
- (void)stopThread
{
    // 設置標記為NO
    self.stopped = YES;
    
    // 停止RunLoop
    CFRunLoopStop(CFRunLoopGetCurrent());
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}

- (void)dealloc
{
    NSLog(@"%s", __func__);
    
    //就算把thread清空也不行鱼鼓,因為任務還沒結束拟烫,線程就不會死。
    //self.thread = nil;
}
@end

注意:上面要使用weakself蚓哩,不然self強引用thread构灸,thread強引用block,block強引用self岸梨,產(chǎn)生循環(huán)引用(使用weakself之后喜颁,就是self強引用thread,thread強引用block曹阔,block弱引用self半开,不會產(chǎn)生循環(huán)引用)。

運行代碼赃份,進入界面寂拆,打印:

<MJThread: 0x60000233af00>{number = 3, name = (null)}----begin----

說明線程開始工作了抓韩。

點擊空白纠永,打印:

-[ViewController test] <MJThread: 0x60000233af00>{number = 3, name = (null)}

說明RunLoop接收到事件谒拴,開始處理事件尝江。

點擊stop打印:

-[ViewController stopThread] <MJThread: 0x6000015f2500>{number = 3, name = (null)}
<MJThread: 0x6000015f2500>{number = 3, name = (null)}----end----

可以看出英上,執(zhí)行了CFRunLoopStop炭序,并且線程任務完成,打印了----end----苍日。

點擊stop之后再退出當前VC惭聂,打印:

-[ViewController dealloc]
-[MJThread dealloc]

可以發(fā)現(xiàn)相恃,當前VC和thread都被銷毀了辜纲。

上面代碼還有一個問題,就是我們每次都要先點擊停止再返回當前VC,這樣很麻煩侨歉,可能你會說可以把[self stop]方法寫在ViewController的dealloc方法里面屋摇,試了下揩魂,發(fā)現(xiàn)報錯壞內存訪問:

壞內存訪問

那到底是誰壞了呢幽邓?稍微想一下也知道是self控制器壞了。
其實原因就是[self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:NO]的最后一個參數(shù)火脉,當傳入NO的時候牵舵,代表不等子線程(self.thread)里面的東西執(zhí)行完,主線程的dealloc方法會接著往下走倦挂,往下走ViewController的dealloc方法就執(zhí)行完了畸颅,self就不在了。
這時候子線程(self.thread)繼續(xù)做事方援,先拿到self對象和stopThread消息没炒,然后在子線程給self對象發(fā)送topThread消息(內部就是通過子線程的RunLoop循環(huán)),這時候self都不在了犯戏,拿不到了送火,所以在子線程的RunLoop循環(huán)里會報錯壞內存訪問。

現(xiàn)在你應該明白為什么會在RunLoop那行代碼報壞內存訪問錯誤了吧先匪!

解決辦法也很簡單种吸,dealloc方法里面調用[self stop],并且將上面NO改成YES呀非。

運行代碼坚俗,直接返回當前VC,打影度埂:

-[ViewController dealloc]
-[ViewController stopThread] <MJThread: 0x600000dda7c0>{number = 3, name = (null)}

可以發(fā)現(xiàn)控制器被銷毀了猖败,CFRunLoopStop也調用了,但是線程還沒死降允,又活了恩闻,這就奇怪了。

其實那個RunLoop的確停掉了拟糕,但是停掉之后判呕,他會再次來到while循環(huán)判斷條件:

while (!weakSelf.isStoped) {
    //beforeDat:過期時間,傳入distantFuture遙遠的未來送滞,就是永遠不會過期
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

這時候當前控制器已經(jīng)被銷毀侠草,weakSelf指針已經(jīng)被清空,這時候!nil獲取的就是YES犁嗅,所以會再次進入循環(huán)體啟動RunLoop边涕,RunLoop又跑起來了,線程又有事情干了,所以線程不會銷毀功蜓。

解決辦法:

while (weakSelf && !weakSelf.isStoped) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

再次運行項目园爷,點擊暫停,返回當前VC式撼,這時候又崩了:

崩了

點擊暫停之后RunLoop肯定停掉了童社,RunLoop停掉后,這時候的線程就不能用了著隆,但是這時候thread還沒銷毀(還沒調用dealloc)扰楼,因為thread還被self引用著,這時候訪問一個不能用的thread就會報壞內存訪問錯誤美浦。

解決辦法也很簡單弦赖,暫停RunLoop后把thread指針置為nil,并且如果發(fā)現(xiàn)子線程為nil就不在子線程做事情了浦辨。

代碼如下:

#import "ViewController.h"
#import "MJThread.h"

@interface ViewController ()
@property (strong, nonatomic) MJThread *thread;
@property (assign, nonatomic, getter=isStoped) BOOL stopped;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    __weak typeof(self) weakSelf = self;
    
    self.stopped = NO;
    self.thread = [[MJThread alloc] initWithBlock:^{
        NSLog(@"%@----begin----", [NSThread currentThread]);
        
        // 往RunLoop里面添加Source\Timer\Observer
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
    
        while (weakSelf && !weakSelf.isStoped) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        
        NSLog(@"%@----end----", [NSThread currentThread]);
    }];
    [self.thread start];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    if (!self.thread) return;
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}

// 子線程需要執(zhí)行的任務
- (void)test
{
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
}

- (IBAction)stop {
    if (!self.thread) return;
    
    // 在子線程調用stop(waitUntilDone設置為YES蹬竖,代表子線程的代碼執(zhí)行完畢后,這個方法才會往下走)
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}

// 用于停止子線程的RunLoop
- (void)stopThread
{
    // 設置標記為YES
    self.stopped = YES;
    
    // 停止RunLoop
    CFRunLoopStop(CFRunLoopGetCurrent());
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    
    // 清空線程
    self.thread = nil;
}

- (void)dealloc
{
    NSLog(@"%s", __func__);
    
    [self stop];
}
@end

二. 線程绷鞒辏活的封裝

上面的代碼雖然實現(xiàn)了線程北也蓿活并且也沒啥bug,但是用起來比較麻煩康吵,下面就封裝一個可控制線程生命周期的類劈榨。

MJPermenantThread.h文件

#import <Foundation/Foundation.h>

//任務的回調
typedef void (^MJPermenantThreadTask)(void);

@interface MJPermenantThread : NSObject

/**
 開啟線程
 */
//- (void)run;

/**
 在當前子線程執(zhí)行一個任務
 */
- (void)executeTask:(MJPermenantThreadTask)task;

/**
 結束線程
 */
- (void)stop;

@end

MJPermenantThread.m文件

#import "MJPermenantThread.h"

/** MJThread **/
@interface MJThread : NSThread
@end
@implementation MJThread
- (void)dealloc
{
    NSLog(@"%s", __func__);
}
@end

/** MJPermenantThread **/
@interface MJPermenantThread()
@property (strong, nonatomic) MJThread *innerThread;
@property (assign, nonatomic, getter=isStopped) BOOL stopped;
@end

@implementation MJPermenantThread
#pragma mark - public methods
- (instancetype)init
{
    if (self = [super init]) {
        self.stopped = NO;
        
        __weak typeof(self) weakSelf = self;
        
        self.innerThread = [[MJThread alloc] initWithBlock:^{
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            
            while (weakSelf && !weakSelf.isStopped) {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }
        }];
        
        //自動開始線程
        [self.innerThread start];
    }
    return self;
}

//- (void)run
//{
//    if (!self.innerThread) return;
//
//    [self.innerThread start];
//}

- (void)executeTask:(MJPermenantThreadTask)task
{
    if (!self.innerThread || !task) return;
    
    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}

- (void)stop
{
    if (!self.innerThread) return;
    
    [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}

//當前對象死了,讓當前對象里面的線程也死
- (void)dealloc
{
    NSLog(@"%s", __func__);
    
    [self stop];
}

#pragma mark - private methods
- (void)__stop
{
    self.stopped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.innerThread = nil;
}

- (void)__executeTask:(MJPermenantThreadTask)task
{
    task();
}

@end

在ViewController里面執(zhí)行以下代碼:

#import "ViewController.h"
#import "MJPermenantThread.h"

@interface ViewController ()
@property (strong, nonatomic) MJPermenantThread *thread;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.thread = [[MJPermenantThread alloc] init];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self.thread executeTask:^{
        NSLog(@"執(zhí)行任務 - %@", [NSThread currentThread]);
    }];
}

- (IBAction)stop {
    [self.thread stop];
}

- (void)dealloc
{
    NSLog(@"%s", __func__);
}

@end

點擊跳轉,跳轉到另外一個界面,在另外一個界面會自動開啟子線程扣蜻,返回界面肴裙,發(fā)現(xiàn)界面和子線程會自動銷毀。

小問題:

保住線程的命為什么要用RunLoop,用強指針不就好了么?

準確來講,使用RunLoop是為了讓線程保持激活狀態(tài)棒妨,雖然用強指針指著它,可以保住線程的命含长,線程不會調用dealloc券腔,這時候線程還在內存中,但是線程的任務一旦執(zhí)行完畢拘泞,生命周期就結束纷纫,無法再使用,已經(jīng)是個廢物了陪腌。所以用強指針保住命沒什么意義辱魁,只能用RunLoop讓線程一直有事可做烟瞧,一直保持激活狀態(tài)。

三. 線程比敬兀活的封裝(C語言)

MJPermenantThread.h文件

#import <Foundation/Foundation.h>

typedef void (^MJPermenantThreadTask)(void);

@interface MJPermenantThread : NSObject

/**
 開啟線程
 */
//- (void)run;

/**
 在當前子線程執(zhí)行一個任務
 */
- (void)executeTask:(MJPermenantThreadTask)task;

/**
 結束線程
 */
- (void)stop;

@end

MJPermenantThread.m文件

#import "MJPermenantThread.h"

/** MJThread **/
@interface MJThread : NSThread
@end
@implementation MJThread
- (void)dealloc
{
    NSLog(@"%s", __func__);
}
@end

/** MJPermenantThread **/
@interface MJPermenantThread()
@property (strong, nonatomic) MJThread *innerThread;
@end

@implementation MJPermenantThread
#pragma mark - public methods
- (instancetype)init
{
    if (self = [super init]) {
        self.innerThread = [[MJThread alloc] initWithBlock:^{
            NSLog(@"begin----");
            
            // 創(chuàng)建上下文(要初始化一下結構體参滴,否則結構體里面有可能是垃圾數(shù)據(jù))
            CFRunLoopSourceContext context = {0};
            
            // 創(chuàng)建source
            CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
            
            // 往Runloop中添加source
            CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
            
            // 銷毀source
            CFRelease(source);
            
            // 啟動
            //參數(shù):模式,過時時間(1.0e10一個很大的值)锻弓,是否執(zhí)行完source后就會退出當前l(fā)oop
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
            
            //如果使用的是C語言的方式就可以通過最后一個參數(shù)讓執(zhí)行完source之后不退出當前Loop砾赔,所以就可以不用stopped屬性了
//            while (weakSelf && !weakSelf.isStopped) {
//                // 第3個參數(shù):returnAfterSourceHandled,設置為true弥咪,代表執(zhí)行完source后就會退出當前l(fā)oop
//                CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
//            }
            
            NSLog(@"end----");
        }];
        
        [self.innerThread start];
    }
    return self;
}

//- (void)run
//{
//    if (!self.innerThread) return;
//
//    [self.innerThread start];
//}

- (void)executeTask:(MJPermenantThreadTask)task
{
    if (!self.innerThread || !task) return;
    
    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}

- (void)stop
{
    if (!self.innerThread) return;
    
    [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}

- (void)dealloc
{
    NSLog(@"%s", __func__);
    
    [self stop];
}

#pragma mark - private methods
- (void)__stop
{
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.innerThread = nil;
}

- (void)__executeTask:(MJPermenantThreadTask)task
{
    task();
}

@end

C語言方式和OC方式達到的效果都是一樣的过蹂,但是C語言方式控制的更精準,可以控制執(zhí)行完source后不退出當前l(fā)oop聚至,這樣就不用寫while循環(huán)了。

Demo地址:線程北境龋活

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末扳躬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子甚亭,更是在濱河造成了極大的恐慌贷币,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亏狰,死亡現(xiàn)場離奇詭異役纹,居然都是意外死亡,警方通過查閱死者的電腦和手機暇唾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門促脉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人策州,你說我怎么就攤上這事瘸味。” “怎么了够挂?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵旁仿,是天一觀的道長。 經(jīng)常有香客問我孽糖,道長枯冈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任办悟,我火速辦了婚禮尘奏,結果婚禮上,老公的妹妹穿的比我還像新娘誉尖。我一直安慰自己罪既,他們只是感情好,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著琢感,像睡著了一般丢间。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驹针,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天烘挫,我揣著相機與錄音,去河邊找鬼柬甥。 笑死饮六,一個胖子當著我的面吹牛,可吹牛的內容都是我干的苛蒲。 我是一名探鬼主播卤橄,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼臂外!你這毒婦竟也來了窟扑?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤漏健,失蹤者是張志新(化名)和其女友劉穎嚎货,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蔫浆,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡殖属,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓦盛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洗显。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖谭溉,靈堂內的尸體忽然破棺而出墙懂,到底是詐尸還是另有隱情,我是刑警寧澤扮念,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布损搬,位于F島的核電站,受9級特大地震影響柜与,放射性物質發(fā)生泄漏巧勤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一弄匕、第九天 我趴在偏房一處隱蔽的房頂上張望颅悉。 院中可真熱鬧,春花似錦迁匠、人聲如沸剩瓶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽延曙。三九已至豌鹤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枝缔,已是汗流浹背布疙。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留愿卸,地道東北人灵临。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像趴荸,于是被迫代替她去往敵國和親儒溉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內容