淺談 iOS NSNotification

NSNotification 解決的問(wèn)題

  • 可以實(shí)現(xiàn)跨層的傳遞,例如A頁(yè)面跳轉(zhuǎn)到B頁(yè)面,B頁(yè)面再跳轉(zhuǎn)到C頁(yè)面,這時(shí)候如果我們通過(guò)委托回調(diào)的模式讓A知道C的一些修改馏段,那么實(shí)現(xiàn)起來(lái)就會(huì)很麻煩。
  • 可以實(shí)現(xiàn)一對(duì)多践瓷,NSNotification 的實(shí)際是一種觀察者模式。

NSNotificationCenter

NSNotificationCenter 就相當(dāng)于一個(gè)廣播站亡蓉,使用 [NSNotificationCenter defaultCenter] 來(lái)獲取晕翠,NSNotificationCenter 實(shí)際上是 iOS 程序內(nèi)部之間的一種消息廣播機(jī)制,主要為了解決應(yīng)用程序內(nèi)部不同對(duì)象之間解耦而設(shè)計(jì)。
NSNotificationCenter 是整個(gè)通知機(jī)制的關(guān)鍵所在淋肾,它管理著監(jiān)聽(tīng)者的注冊(cè)和注銷硫麻,通知的發(fā)送和接收。NSNotificationCenter 維護(hù)著一個(gè)通知的分發(fā)表樊卓,把所有通知發(fā)送者發(fā)送的通知拿愧,轉(zhuǎn)發(fā)給對(duì)應(yīng)的監(jiān)聽(tīng)者們。每一個(gè) iOS 程序都有一個(gè)唯一的通知中心碌尔,不必自己去創(chuàng)建一個(gè)浇辜,它是一個(gè)單例,通過(guò) [NSNotificationCenter defaultCenter] 方法獲取唾戚。
NSNotificationCenter 是基于觀察者模式設(shè)計(jì)的柳洋,不能跨應(yīng)用程序進(jìn)程通信,當(dāng) NSNotificationCenter 接收到消息之后會(huì)根據(jù)內(nèi)部的消息轉(zhuǎn)發(fā)表叹坦,將消息發(fā)送給訂閱者熊镣;它可以向應(yīng)用任何地方發(fā)送和接收通知。
NSNotificationCenter 注冊(cè)觀察者募书,發(fā)送者使用通知中心廣播時(shí)绪囱,以 NSNotificationnameobject 來(lái)確定需要發(fā)送給哪個(gè)觀察者。為保證觀察者能接收到通知莹捡,所以應(yīng)先向通知中心注冊(cè)觀察者鬼吵,接著再發(fā)送通知這樣才能在通知中心調(diào)度表中查找到相應(yīng)觀察者進(jìn)行通知。

NSNotification

NSNotificationNSNotificationCenter 接收到消息之后根據(jù)內(nèi)部的消息轉(zhuǎn)發(fā)表道盏,將消息發(fā)送給訂閱者封裝的對(duì)象而柑;

@interface NSNotification : NSObject <NSCopying, NSCoding>

//這個(gè)成員變量是這個(gè)消息對(duì)象的唯一標(biāo)識(shí),用于辨別消息對(duì)象
@property (readonly, copy) NSString *name;
// 這個(gè)成員變量定義一個(gè)對(duì)象荷逞,可以理解為針對(duì)某一個(gè)對(duì)象的消息媒咳,代表通知的發(fā)送者
@property (nullable, readonly, retain) id object;
//這個(gè)成員變量是一個(gè)字典,可以用其來(lái)進(jìn)行傳值
@property (nullable, readonly, copy) NSDictionary *userInfo;
// 初始化方法
- (instancetype)initWithName:(NSString *)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo NS_AVAILABLE(10_6, 4_0) NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;

@end

由于 NSNotification 屬性都是只讀的种远,如果要?jiǎng)?chuàng)建通知?jiǎng)t要用下面 NSNotification(NSNotificationCreation) 分類相應(yīng)的方法進(jìn)行初始化涩澡;

NSNotification 不能通過(guò) init 實(shí)例化,這樣會(huì)引起下面的異常坠敷,比如:

NSNotification *notif = [[NSNotification alloc] init];

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** -[NSConcreteNotification init]: should never be used'
@interface NSNotification (NSNotificationCreation)
+ (instancetype)notificationWithName:(NSString *)aName object:(nullable id)anObject;
+ (instancetype)notificationWithName:(NSString *)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
- (instancetype)init /*NS_UNAVAILABLE*/;    /* do not invoke; not a valid initializer for this class */
@end

注意:
如果 NSNotification 對(duì)象中的 notificationNamenil妙同,則會(huì)接收所有的通知。通知中心是以 NSNotificationnameobject 來(lái)確定需要發(fā)送給哪個(gè)觀察者膝迎。監(jiān)聽(tīng)同一條通知的多個(gè)觀察者粥帚,在通知到達(dá)時(shí),它們執(zhí)行回調(diào)的順序是不確定的限次,所以我們不能去假設(shè)操作的執(zhí)行會(huì)按照添加觀察者的順序來(lái)執(zhí)行芒涡。

通知中心默認(rèn)是以同步的方式發(fā)送通知的柴灯,也就是說(shuō),當(dāng)一個(gè)對(duì)象發(fā)送了一個(gè)通知费尽,只有當(dāng)該通知的所有接受者都接受到了通知中心分發(fā)的通知消息并且處理完成后赠群,發(fā)送通知的對(duì)象才能繼續(xù)執(zhí)行接下來(lái)的方法。

NSNotificationQueue

NSNotificationQueue 通知隊(duì)列旱幼,用來(lái)管理多個(gè)通知的調(diào)用查描。通知隊(duì)列通常以先進(jìn)先出 FIFO 順序維護(hù)通。NSNotificationQueue 就像一個(gè)緩沖池把一個(gè)個(gè)通知放進(jìn)池子中柏卤,使用特定方式通過(guò) NSNotificationCenter 發(fā)送到相應(yīng)的觀察者冬三。下面我們會(huì)提到特定的方式即合并通知和異步通知。

創(chuàng)建通知隊(duì)列方法:

- (instancetype)initWithNotificationCenter:(NSNotificationCenter *)notificationCenter NS_DESIGNATED_INITIALIZER;

往隊(duì)列加入通知方法:

- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle;
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle coalesceMask:(NSNotificationCoalescing)coalesceMask forModes:(nullable NSArray<NSRunLoopMode> *)modes;

移除隊(duì)列中的通知方法:

- (void)dequeueNotificationsMatching:(NSNotification *)notification coalesceMask:(NSUInteger)coalesceMask;
發(fā)送方式

NSPostingStyle包括三種類型:

typedef NS_ENUM(NSUInteger, NSPostingStyle) {
    NSPostWhenIdle = 1,
    NSPostASAP = 2,
    NSPostNow = 3  
};

NSPostWhenIdle:空閑發(fā)送通知闷旧,當(dāng)運(yùn)行循環(huán)處于等待或空閑狀態(tài)時(shí)长豁,發(fā)送通知,對(duì)于不重要的通知可以使用忙灼。
NSPostASAP:盡快發(fā)送通知匠襟,當(dāng)前運(yùn)行循環(huán)迭代完成時(shí),通知將會(huì)被發(fā)送该园,有點(diǎn)類似沒(méi)有延遲的定時(shí)器酸舍。
NSPostNow :同步發(fā)送通知,如果不使用合并通知和 postNotification: 一樣是同步通知里初。

合并通知

NSNotificationCoalescing也包括三種類型:

typedef NS_OPTIONS(NSUInteger, NSNotificationCoalescing) {
    NSNotificationNoCoalescing = 0,
    NSNotificationCoalescingOnName = 1,
    NSNotificationCoalescingOnSender = 2
};

NSNotificationNoCoalescing:不合并通知啃勉。
NSNotificationCoalescingOnName:合并相同名稱的通知。
NSNotificationCoalescingOnSender:合并相同通知和同一對(duì)象的通知双妨。

通過(guò)合并我們可以用來(lái)保證相同的通知只被發(fā)送一次淮阐。forModes:(nullable NSArray<NSRunLoopMode> *)modes 可以使用不同的 NSRunLoopMode 配合來(lái)發(fā)送通知,可以看出實(shí)際上 NSNotificationQueueRunLoop 的機(jī)制以及運(yùn)行循環(huán)有關(guān)系刁品,通過(guò) NSNotificationQueue 隊(duì)列來(lái)發(fā)送的通知和關(guān)聯(lián)的 RunLoop 運(yùn)行機(jī)制來(lái)進(jìn)行的泣特。

iOS 9 NSNotificationCenter 無(wú)需手動(dòng)移除觀察者

眾所周知,在觀察者對(duì)象釋放之前挑随,需要調(diào)用 removeObserver 方法状您,將觀察者從通知中心移除,否則程序可能會(huì)出現(xiàn)崩潰兜挨。其實(shí)膏孟,從 iOS 9 開(kāi)始,即使不移除觀察者對(duì)象拌汇,程序也不會(huì)出現(xiàn)異常柒桑。這是為什么呢?我們先了解一下噪舀,為什么 iOS 9 之前需要手動(dòng)移除觀察者對(duì)象幕垦。

MRC 時(shí)代丢氢,觀察者注冊(cè)時(shí)傅联,通知中心并不會(huì)對(duì)觀察者對(duì)象做 retain 操作先改,而是對(duì)觀察者對(duì)象進(jìn)行 unsafe_unretained 引用。

// for attribute
@property (unsafe_unretained) NSObject *unsafeProperty;
// for variables
NSObject *__unsafe_unretained unsafeReference;

不安全引用(unsafe reference)和弱引用 (weak reference) 類似蒸走,它并不會(huì)讓被引用的對(duì)象保持存活仇奶,但是和弱引用不同的是,當(dāng)被引用的對(duì)象釋放的時(shí)比驻,不安全引用并不會(huì)自動(dòng)被置為 nil该溯,這就意味著它變成了野指針,而對(duì)野指針發(fā)送消息會(huì)導(dǎo)致程序崩潰别惦。

If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method.

而在 iOS 9 以后狈茉,通知中心持有的是注冊(cè)者的 weak 指針,這時(shí)即使不對(duì)通知進(jìn)行手動(dòng)移除掸掸,指針也會(huì)在注冊(cè)者被回收后自動(dòng)置空氯庆。但是,通過(guò) -[NSNotificationCenter addObserverForName:object:queue:usingBlock] 方法注冊(cè)的觀察者依然需要手動(dòng)的釋放扰付,因?yàn)橥ㄖ行膶?duì)它們持有的是強(qiáng)引用堤撵。

NSNotification在多線程中使用

在多線程中,無(wú)論在哪個(gè)線程注冊(cè)了觀察者羽莺,Notification 接收和處理都是在發(fā)送 Notification 的線程中的实昨。所以,當(dāng)我們需要在接收到 Notification 后作出更新 UI 操作的話盐固,就需要考慮線程的問(wèn)題了荒给,如果在子線程中發(fā)送 Notification,想要在接收到 Notification 后更新 UI 的話就要切換回到主線程刁卜。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *NOTIFICATION_NAME = @"NOTIFICATION_NAME";
    
    NSLog(@"Current thread = %@", [NSThread currentThread]);
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:NOTIFICATION_NAME object:nil];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
         NSLog(@"Post notification志电,Current thread = %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_NAME object:nil userInfo:nil];
    });
}
 
- (void)handleNotification:(NSNotification *)notification {
    NSLog(@"Receive notification,Current thread = %@", [NSThread currentThread]);
}

運(yùn)行結(jié)果:

2017-03-11 17:56:33.898 NotificationTest[23457:1615587] Current thread = <NSThread: 0x608000078080>{number = 1, name = main}
2017-03-11 17:56:33.899 NotificationTest[23457:1615738] Post notification长酗,Current thread = <NSThread: 0x60000026c500>{number = 3, name = (null)}
2017-03-11 17:56:33.899 NotificationTest[23457:1615738] Receive notification溪北,Current thread = <NSThread: 0x60000026c500>{number = 3, name = (null)}

上面我們?cè)谥骶€程注冊(cè)觀察者,在子線程發(fā)送 Notification夺脾,最后 Notification 的接收和處理也是在子線程之拨。

注意:
在一個(gè)多線程的程序中,發(fā)送方發(fā)送通知的線程通常就是監(jiān)聽(tīng)者接受通知的線程咧叭,這可能和監(jiān)聽(tīng)者注冊(cè)時(shí)的線程不一樣蚀乔。

解決方法
MachPort的使用方式

最好的方法是在 Notification 所在的默認(rèn)線程中捕獲發(fā)送的通知,然后將其重定向到指定的線程中菲茬。關(guān)于 Notification 的重定向官方文檔給出了一個(gè)方法:

一種重定向的實(shí)現(xiàn)思路是自定義一個(gè)通知隊(duì)列(不是 NSNotificationQueue 對(duì)象)吉挣,讓這個(gè)隊(duì)列去維護(hù)那些我們需要重定向的 Notification派撕。我們?nèi)匀皇窍裰耙粯尤プ?cè)一個(gè)通知的觀察者,當(dāng) Notification 到達(dá)時(shí)睬魂,先看看 post 這個(gè) Notification 的線程是不是我們所期望的線程终吼,如果不是,就將這個(gè) Notification 放到我們的隊(duì)列中氯哮,然后發(fā)送一個(gè)信號(hào)signal到期望的線程中际跪,來(lái)告訴這個(gè)線程需要處理一個(gè) Notification。指定的線程收到這個(gè)信號(hào)signal后喉钢,將 Notification 從隊(duì)列中移除姆打,并進(jìn)行后續(xù)處理。

//  ViewController.m
//  NotificationTest
//
//  Created by sunjinshuai on 2017/3/11.
//  Copyright ? 2017年 sunjinshuai. All rights reserved.
//
 
#import "ViewController.h"
 
@interface ViewController ()<NSMachPortDelegate>
 
@property (nonatomic) NSMutableArray    *notifications;         // 通知隊(duì)列
@property (nonatomic) NSThread          *notificationThread;    // 想要處理通知的線程(目標(biāo)線程)
@property (nonatomic) NSLock            *notificationLock;      // 用于對(duì)通知隊(duì)列加鎖的鎖對(duì)象肠虽,避免線程沖突
@property (nonatomic) NSMachPort        *notificationPort;      // 用于向目標(biāo)線程發(fā)送信號(hào)的通信端口
 
@end
 
@implementation ViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *NOTIFICATION_NAME = @"NOTIFICATION_NAME";
 
    NSLog(@"Current thread = %@", [NSThread currentThread]);
    
    [self setUpThreadingSupport];
    
    // 注冊(cè)觀察者
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(processNotification:) name:NOTIFICATION_NAME object:nil];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 發(fā)送Notification
        NSLog(@"Post notification幔戏,Current thread = %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_NAME object:nil userInfo:nil];
        
    });
}
 
/*
    在注冊(cè)任何通知之前,需要先初始化屬性税课。下面方法初始化了隊(duì)列和鎖定對(duì)象闲延,保留對(duì)當(dāng)前線程對(duì)象的引用,并創(chuàng)建一個(gè)Mach通信端口伯复,將其添加到當(dāng)前線程的運(yùn)行循環(huán)中慨代。
    此方法運(yùn)行后,發(fā)送到notificationPort的任何消息都會(huì)在首次運(yùn)行此方法的線程的run loop中接收啸如。如果接收線程的run loop在Mach消息到達(dá)時(shí)沒(méi)有運(yùn)行侍匙,則內(nèi)核保持該消息,直到下一次進(jìn)入run loop叮雳。接收線程的run loop將傳入消息發(fā)送到端口delegate的handleMachMessage:方法想暗。
 */
- (void)setUpThreadingSupport {
    if (self.notifications) {
        return;
    }
    self.notifications      = [[NSMutableArray alloc] init];
    self.notificationLock   = [[NSLock alloc] init];
    self.notificationThread = [NSThread currentThread];
    
    self.notificationPort = [[NSMachPort alloc] init];
    [self.notificationPort setDelegate:self];
    [[NSRunLoop currentRunLoop] addPort:self.notificationPort
                                forMode:(__bridge NSString*)kCFRunLoopCommonModes];
}
 
 
/**
 端口的代理方法
 */
- (void)handleMachMessage:(void *)msg {
    
    [self.notificationLock lock];
    
    while ([self.notifications count]) {
        NSNotification *notification = [self.notifications objectAtIndex:0];
        [self.notifications removeObjectAtIndex:0];
        [self.notificationLock unlock];
        [self processNotification:notification];
        [self.notificationLock lock];
    };
    
    [self.notificationLock unlock];
}
 
- (void)processNotification:(NSNotification *)notification {
    
    //判斷是不是目標(biāo)線程,不是則轉(zhuǎn)發(fā)到目標(biāo)線程
    if ([NSThread currentThread] != _notificationThread) {
        // 將Notification轉(zhuǎn)發(fā)到目標(biāo)線程
        [self.notificationLock lock];
        [self.notifications addObject:notification];
        [self.notificationLock unlock];
        [self.notificationPort sendBeforeDate:[NSDate date]
                                   components:nil
                                         from:nil
                                     reserved:0];
    } else {
        // 在此處理通知
        NSLog(@"Receive notification帘不,Current thread = %@", [NSThread currentThread]);
        NSLog(@"Process notification");
    }
}
 
@end

打印結(jié)果:

2017-03-11 18:28:55.788 NotificationTest[24080:1665269] Current thread = <NSThread: 0x60800006d4c0>{number = 1, name = main}
2017-03-11 18:28:55.789 NotificationTest[24080:1665396] Post notification说莫,Current thread = <NSThread: 0x60800026bc40>{number = 4, name = (null)}
2017-03-11 18:28:55.795 NotificationTest[24080:1665269] Receive notification,Current thread = <NSThread: 0x60800006d4c0>{number = 1, name = main}
2017-03-11 18:28:55.795 NotificationTest[24080:1665269] Process notification

在發(fā)送通知的子線程處理通知的事件時(shí)寞焙,將 NSNotification 暫存储狭,然后通過(guò) MachPort 往相應(yīng)線程的 RunLoop 中發(fā)送事件。相應(yīng)的線程收到該事件后捣郊,取出在隊(duì)列中暫存的 NSNotification , 然后在當(dāng)前線程中調(diào)用處理通知的方法辽狈。
可以看到,運(yùn)行結(jié)果結(jié)果我們想要的:在子線程中發(fā)送 Notification呛牲,在主線程中接收與處理 Notification刮萌。

上面的實(shí)現(xiàn)方法也不是絕對(duì)完美的,蘋果官方指出了這種方法的限制:

  • 所有線程的 Notification 的處理都必須通過(guò)相同的方法 processNotification:娘扩。
  • 每個(gè)對(duì)象必須提供自己的實(shí)現(xiàn)和通信端口着茸。
block

上面蘋果官方給我們提供的方法外壮锻,我們還可以利用基于 blockNSNotification 去實(shí)現(xiàn),appleios4 之后提供了帶有 blockNSNotification涮阔。使用方式如下:

 - (id<NSObject>)addObserverForName:(NSString *)name
                            object:(id)obj
                             queue:(NSOperationQueue *)queue
                        usingBlock:(void (^)(NSNotification *note))block

其中:

  • 觀察者就是當(dāng)前對(duì)象
  • queue 定義了 block 執(zhí)行的線程猜绣,nil 則表示 block 的執(zhí)行線程和發(fā)通知在同一個(gè)線程
  • block 就是相應(yīng)通知的處理函數(shù)

這個(gè) API 已經(jīng)能夠讓我們方便的控制通知的線程切換。但是澎语,這里有個(gè)問(wèn)題需要注意途事。就是其 remove 操作。

原來(lái)的 NSNotificationremove 方式如下:

- (void)removeObservers {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:POST_NOTIFICATION object:nil];
}

但是帶 block 方式的 remove 便不能像上面這樣處理了擅羞。其方式如下:

- (void)removeObservers {
    if(_observer){
        [[NSNotificationCenter defaultCenter] removeObserver:_observer];
    }
}

其中 _observeraddObserverForName 方式的 api 返回觀察者對(duì)象。這也就意味著义图,你需要為每一個(gè)觀察者記錄一個(gè)成員對(duì)象减俏,然后在 remove 的時(shí)候依次刪除。試想一下碱工,你如果需要 10 個(gè)觀察者娃承,則需要記錄 10 個(gè)成員對(duì)象,這個(gè)想想就是很麻煩怕篷,而且它還不能夠方便的指定 observer 历筝。因此,理想的做法就是自己再做一層封裝廊谓,將這些細(xì)節(jié)封裝起來(lái)梳猪。

當(dāng)然,想要在子線程發(fā)送 Notification蒸痹、接收到 Notification 后在主線程中做后續(xù)操作春弥,可以用一個(gè)很笨的方法,在 handleNotification 里面強(qiáng)制切換線程:

- (void)handleNotification:(NSNotification *)notification {
   NSLog(@"Receive notification叠荠,Current thread = %@", [NSThread currentThread]);
   dispatch_async(dispatch_get_main_queue(), ^{
      NSLog(@"Current thread = %@", [NSThread currentThread]);
   });
}

在簡(jiǎn)單情況下可以使用這種方法匿沛,但是當(dāng)我們發(fā)送了多個(gè) Notification 并且有多個(gè)觀察者的時(shí)候,難道我們要在每個(gè)地方都手動(dòng)切換線程榛鼎?所以逃呼,這種方法并不是一個(gè)有效的方法。

通知的實(shí)現(xiàn)原理

以下源碼來(lái)自于libs-base

typedef struct NCTbl {
  Observation       *wildcard;  /* Get ALL messages.        */
  GSIMapTable       nameless;   /* Get messages for any name.   */
  GSIMapTable       named;      /* Getting named messages only. */
  unsigned      lockCount;  /* Count recursive operations.  */
  NSRecursiveLock   *_lock;     /* Lock out other threads.  */
  Observation       *freeList;
  Observation       **chunks;
  unsigned      numChunks;
  GSIMapTable       cache[CACHESIZE];
  unsigned short    chunkIndex;
  unsigned short    cacheIndex;
} NCTable;

typedef struct  Obs {
  id        observer;   /* Object to receive message.   */
  SEL       selector;   /* Method selector.     */
  struct Obs    *next;      /* Next item in linked list.    */
  int       retained;   /* Retain count for structure.  */
  struct NCTbl  *link;      /* Pointer back to chunk table  */
} Observation;

從源碼中可以看出者娱,在 NSNotificationCenter 內(nèi)部一共保存了兩張 MapTable抡笼。一張用于保存添加觀察者的時(shí)候傳入了 NotifcationName 的情況;一張用于保存添加觀察者的時(shí)候沒(méi)有傳入了 NotifcationName 的情況肺然。

Example

https://github.com/iOS-Advanced/iOS-Advanced/tree/master/sourcecode/NSNotificationTheory

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蔫缸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子际起,更是在濱河造成了極大的恐慌拾碌,老刑警劉巖吐葱,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異校翔,居然都是意外死亡弟跑,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門防症,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)孟辑,“玉大人,你說(shuō)我怎么就攤上這事蔫敲∷撬裕” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵奈嘿,是天一觀的道長(zhǎng)貌虾。 經(jīng)常有香客問(wèn)我,道長(zhǎng)裙犹,這世上最難降的妖魔是什么尽狠? 我笑而不...
    開(kāi)封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮叶圃,結(jié)果婚禮上袄膏,老公的妹妹穿的比我還像新娘。我一直安慰自己掺冠,他們只是感情好沉馆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著赫舒,像睡著了一般悍及。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上接癌,一...
    開(kāi)封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天心赶,我揣著相機(jī)與錄音,去河邊找鬼缺猛。 笑死缨叫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荔燎。 我是一名探鬼主播耻姥,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼有咨!你這毒婦竟也來(lái)了琐簇?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎婉商,沒(méi)想到半個(gè)月后似忧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丈秩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年盯捌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蘑秽。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饺著,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肠牲,到底是詐尸還是另有隱情幼衰,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布埂材,位于F島的核電站塑顺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏俏险。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一扬绪、第九天 我趴在偏房一處隱蔽的房頂上張望竖独。 院中可真熱鬧,春花似錦挤牛、人聲如沸莹痢。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)竞膳。三九已至,卻和暖如春诫硕,著一層夾襖步出監(jiān)牢的瞬間坦辟,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工章办, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锉走,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓藕届,卻偏偏與公主長(zhǎng)得像挪蹭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子休偶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

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