iOS響應(yīng)者鏈徹底掌握2019-04-22

概述

iOS響應(yīng)者鏈(Responder Chain)是支撐App界面交互的重要基礎(chǔ)饵较,點(diǎn)擊、滑動(dòng)遭赂、旋轉(zhuǎn)循诉、搖晃等都離不開其背后的響應(yīng)者鏈,所以每個(gè)iOS開發(fā)人員都應(yīng)該徹底掌握響應(yīng)者鏈的響應(yīng)邏輯撇他,本文旨在通過demo測試的方式展現(xiàn)響應(yīng)者鏈的具體響應(yīng)過程茄猫,幫助讀者徹底掌握響應(yīng)者鏈。

Demo

你可以在這里(GitHub地址)下載本文測試的Demo源碼困肩,閱讀本文的同時(shí)結(jié)合Demo程序有助于更加直觀深刻的理解划纽。

探究過程

響應(yīng)者(Responder)

當(dāng)我們觸控手機(jī)屏幕時(shí)系統(tǒng)便會(huì)將這一操作封裝成一個(gè)UIEvent放到事件隊(duì)列里面,然后Application從事件隊(duì)列取出這個(gè)事件锌畸,接著需要找到去響應(yīng)這個(gè)事件的最佳視圖也就是Responder, 所以開始的第一步應(yīng)該是找到Responder, 那么又是如何找到的呢勇劣?那就不得不引出UIView的2個(gè)方法:

  • -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    返回視圖層級中能響應(yīng)觸控點(diǎn)的最深視圖
  • -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    返回視圖是否包含指定的某個(gè)點(diǎn)
    通過在顯示視圖層級中依次對視圖調(diào)用這個(gè)2個(gè)方法來確認(rèn)該視圖是不是能響應(yīng)這個(gè)點(diǎn)擊的點(diǎn),首先會(huì)調(diào)用hitTest潭枣,然后hitTest會(huì)調(diào)用pointInside芭毙,最終hitTest返回的那個(gè)view就是最終的響應(yīng)者Responder, 那么問題來了,在視圖層級中是如何確定該對哪個(gè)View調(diào)用呢卸耘?優(yōu)先級又是什么?
    為了探尋其中的邏輯粘咖,在Demo中我們構(gòu)建了一個(gè)如下圖所示的多重視圖:
4288200824-5b07d29cd9408_articlex.jpeg

這是一個(gè)簡單的控制器視圖蚣抗,在Controller的視圖上添加了View1-View4共4個(gè)視圖,View1-View4和RootView都繼承自BaseView瓮下, BaseView繼承自UIView; 其中 View1翰铡、View2是RootView的子視圖,View3讽坏、View4是View2的子視圖,他們的繼承關(guān)系和父子關(guān)系圖下圖:

1435560615-5b07d7cd5d4d1_articlex.jpeg

為了能觀測到UIView的hitTest和pointInside調(diào)用過程锭魔,我們寫個(gè)分類通過方法交換來打印調(diào)用的日志:

@implementation UIView (DandJ)
+ (void)load {
    Method origin = class_getInstanceMethod([UIView class], @selector(hitTest:withEvent:));
    Method custom = class_getInstanceMethod([UIView class], @selector(dandJ_hitTest:withEvent:));
    method_exchangeImplementations(origin, custom);

    origin = class_getInstanceMethod([UIView class], @selector(pointInside:withEvent:));
    custom = class_getInstanceMethod([UIView class], @selector(dandJ_pointInside:withEvent:));
    method_exchangeImplementations(origin, custom);
}

- (UIView *)dandJ_hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    NSLog(@"%@ hitTest", NSStringFromClass([self class]));
    UIView *result = [self dandJ_hitTest:point withEvent:event];
    NSLog(@"%@ hitTest return: %@", NSStringFromClass([self class]), NSStringFromClass([result class]));
    return result;
}

- (BOOL)dandJ_pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    NSLog(@"%@ pointInside", NSStringFromClass([self class]));
    BOOL result = [self dandJ_pointInside:point withEvent:event];
    NSLog(@"%@ pointInside return: %@", NSStringFromClass([self class]), result ? @"YES":@"NO");
    return result;
}

@end

當(dāng)我們點(diǎn)擊視圖中的View3(紫色)時(shí)看看日志輸出:

1977433700-5b07dc4dc6d0f_articlex.jpeg

從日志中我們可以看到,首先是從UIWindow開始調(diào)用hitTest, 然后經(jīng)過一段導(dǎo)航控制器的視圖路呜,因?yàn)槲覀兊目刂破魇窃趯?dǎo)航控制的迷捧,所以可以先忽略這一段织咧,然后來到RootView,調(diào)用RootView的hitTest和pointInside,因?yàn)辄c(diǎn)擊發(fā)生在RootView中所以繼續(xù)遍歷它的子視圖,可以看到是從View2開始的漠秋,調(diào)用View2的hitTest和pointInside笙蒙,pointInside返回YES,然后繼續(xù)遍歷View2的子視圖庆锦,從View4開始捅位,因?yàn)辄c(diǎn)擊不發(fā)生在View4所以pointInside返回NO,而View4沒有子視圖了,所以返回了nil也就是打印出來的null,然后繼續(xù)在View2的另外一個(gè)子視圖View3(目標(biāo)視圖)中調(diào)用hitTest和pointInside搂抒,因?yàn)槲覀凕c(diǎn)擊的就是View3所以pointInside返回YES,且View3沒有子視圖所以hitTest返回了自己View3,接著View2的hitTest也返回View3直到UIWindow返回View3, 自此我們找到了響應(yīng)視圖:View3艇搀!另外我們看到對其他的Window也有調(diào)用,只不過返回了nil求晶。

  • 結(jié)論:
  1. 尋找事件的最佳響應(yīng)視圖是通過對視圖調(diào)用hitTest和pointInside完成的
  2. hitTest的調(diào)用順序是從UIWindow開始焰雕,對視圖的每個(gè)子視圖依次調(diào)用,子視圖的調(diào)用順序是從后面往前面誉帅,也可以說是從顯示最上面到最下面
  3. 遍歷直到找到響應(yīng)視圖淀散,然后逐級返回最終到UIWindow返回此視圖
    PS:
    1.關(guān)于最后一個(gè)能響應(yīng)的子視圖demo中是因?yàn)闆]有子視圖而確定的,這不是唯一確定的條件,因?yàn)橛行┣闆r下視圖可能會(huì)被忽略蚜锨,不會(huì)調(diào)用hitTest档插,這與userInteractionEnabled, alpha, frame等有關(guān),在下個(gè)demo會(huì)演示亚再。
    2.與加速度器郭膛、陀螺儀、磁力儀相關(guān)的運(yùn)動(dòng)事件不遵循此響應(yīng)鏈氛悬,他們是由Core Motion 直接派發(fā)的

處理者

在上面我們已經(jīng)找到了點(diǎn)擊事件的響應(yīng)者View3,但是我們并未給View3添加相應(yīng)的點(diǎn)擊處理邏輯(UITapGestureRecognizer)则剃,所以View3并不會(huì)處理事件,那么View3不處理由會(huì)交給誰處理呢如捅?如果View3處理了又是怎么樣的呢棍现?
能夠處理UI事件都是繼承UIResponder的子類對象,UIResponder主要有以下4個(gè)方法來處理事件:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

分別是對應(yīng)從觸摸事件的開始镜遣、移動(dòng)己肮、結(jié)束、取消悲关,如果你想自定義響應(yīng)事件可以重寫這幾個(gè)方法來實(shí)現(xiàn)谎僻。如果某個(gè)Responder沒處理事件,事件會(huì)被傳遞寓辱,UIResponder都有一個(gè)nextResponder屬性艘绍,此屬性會(huì)返回在Responder Chain中的下一個(gè)事件處理者,如果每個(gè)Responder都不處理事件秫筏,那么事件將會(huì)被丟棄诱鞠。所以繼承自UIResponder的子類便會(huì)構(gòu)成一條響應(yīng)者鏈挎挖,所以我們可以打印下以View3為開始的響應(yīng)者鏈?zhǔn)鞘裁礃拥模?/p>

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    UIResponder *nextResponder = self.view3.nextResponder;
    NSMutableString *pre = [NSMutableString stringWithString:@"--"];
    NSLog(@"View3");
    while (nextResponder) {
        NSLog(@"%@%@", pre, NSStringFromClass([nextResponder class]));
        [pre appendString:@"--"];
        nextResponder = nextResponder.nextResponder;
    }
}
3420427165-5b07e8f1231d3_articlex.png

可以看到響應(yīng)者鏈一直延伸到AppDelegate, View3的下一個(gè)是View2也就是View3的父視圖,View2下一個(gè)是RootView也是父視圖般甲,而RootView的下一個(gè)則是Controller, 所以下一個(gè)響應(yīng)者的規(guī)則是如果有父視圖則nextResponder指向父視圖肋乍,如果是控制器根視圖則指向控制器,控制器如果在導(dǎo)航控制器中則指向?qū)Ш娇刂破鞯南嚓P(guān)顯示視圖最后指向?qū)Ш娇刂破鞣蟠妫绻歉刂破鲃t指向UIWindow,UIWindow的nexResponder指向UIApplication最后指向AppDelegate,而他們實(shí)現(xiàn)這一套指向都是靠重寫nextReponder實(shí)現(xiàn)的墓造。

為了驗(yàn)證點(diǎn)擊上面的事件的處理順序,我們繼續(xù)上面那個(gè)demo,為RootView和View1-View4的基類BaseView重寫這幾個(gè)方法:

@implementation BaseView

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"%@ touchesBegan", NSStringFromClass([self class]));
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"%@ touchesMoved", NSStringFromClass([self class]));
    [super touchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"%@ touchesEnded", NSStringFromClass([self class]));
    [super touchesEnded:touches withEvent:event];
}

@end

同樣也為控制器(FindResponderController)添加相關(guān)touches方法锚烦,日志打印看調(diào)用順序:

1502820978-5b0a5947ed99d_articlex.png

可以看到先是由UIWindow通過hitTest返回所找到的最合適的響應(yīng)者View3, 接著執(zhí)行了View3的touchesBegan觅闽,然后是通過nextResponder依次是View2、RootView涮俄、FindResponderController,可以看到完全是按照nextResponder鏈條的調(diào)用順序蛉拙,touchesEnded也是同樣的順序。

PS:感興趣的可以繼續(xù)重寫AppDelegate的相關(guān)touches方法彻亲,驗(yàn)證最終是不是會(huì)被順序調(diào)用孕锄。

上面是View3不處理點(diǎn)擊事件的情況,接下來我們?yōu)閂iew3添加一個(gè)點(diǎn)擊事件處理苞尝,看看又會(huì)是什么樣的調(diào)用過程:

@implementation View3
- (void)awakeFromNib {
    [super awakeFromNib];
    [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)]];
}

- (void)tapAction:(UITapGestureRecognizer *)recognizer {
    NSLog(@"View3 taped");
}

@end

運(yùn)行程序畸肆,點(diǎn)擊View3看看日志打印:

340777262-5b0a5ae241540_articlex.png

可以看到touchesBegan順著nextResponder鏈條調(diào)用了宙址,但是View3處理了事件轴脐,去執(zhí)行了相關(guān)是事件處理方法,而touchesEnded并沒有得到調(diào)用抡砂。

總結(jié)

1.找到最適合的響應(yīng)視圖后事件會(huì)從此視圖開始沿著響應(yīng)鏈nextResponder傳遞大咱,直到找到處理事件的視圖,如果沒有處理的事件會(huì)被丟棄。
2.如果視圖有父視圖則nextResponder指向父視圖注益,如果是根視圖則指向控制器碴巾,最終指向AppDelegate, 他們都是通過重寫nextResponder來實(shí)現(xiàn)。

無法響應(yīng)的情況

在[響應(yīng)者]章節(jié)我們已經(jīng)提到尋找最佳響應(yīng)者是通過hitTest函數(shù)調(diào)用完成的丑搔,那么存在哪些情況下視圖會(huì)被忽視厦瓢,而不被調(diào)用hiTest呢?
下面我么也通過第2個(gè)demo來演示低匙,在什么情況下hitTest不會(huì)被調(diào)用或者返回nil,在demo中從上到下我們分別模擬了Alpha=0、子視圖超出父視圖的情況碳锈、userInteractionEnabled=NO顽冶、hidden=YES這4中情況:

1130107321-5b0b6f7796245_articlex.png

結(jié)論

1.Alpha=0、子視圖超出父視圖的情況售碳、userInteractionEnabled=NO强重、hidden=YES視圖會(huì)被忽略绞呈,不會(huì)調(diào)用hitTest
2.父視圖被忽略后其所有子視圖也會(huì)被忽略,所以View3上的button不會(huì)有點(diǎn)擊反應(yīng)
3.出現(xiàn)視圖無法響應(yīng)的情況间景,可以考慮上訴情況來排查問題

應(yīng)用示例

點(diǎn)擊透傳

RootView有2個(gè)重疊在一起的子視圖View1和View2, View2覆蓋在View1上面佃声,如何做到點(diǎn)擊View1觸發(fā)View2的處理邏輯?
很簡單倘要,設(shè)置View2的userInteractionEnabled=NO即可圾亏。
限定點(diǎn)擊區(qū)域
給定一個(gè)顯示為圓形的視圖,實(shí)現(xiàn)只有在點(diǎn)擊區(qū)域在圓形里面才視為有效封拧。
我們可以重寫View的pointInside方法來判斷點(diǎn)擊的點(diǎn)是否在圓內(nèi)志鹃,也就是判斷點(diǎn)擊的點(diǎn)到圓心的距離是否小于等于半徑就可以。

@implementation CircleView
- (void)awakeFromNib {
    [super awakeFromNib];
    self.layer.cornerRadius = self.frame.size.width / 2.0f;
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    const CGFloat radius = self.frame.size.width / 2.0f;
    CGFloat xOffset = point.x - radius;
    CGFloat yOffset = point.y - radius;
    CGFloat distance = sqrt(xOffset * xOffset + yOffset * yOffset);
    return distance <= radius;
}
@end

個(gè)人理解與總結(jié)

1泽西、概述

首先曹铃,當(dāng)發(fā)生事件響應(yīng)時(shí),必須知道由誰來響應(yīng)事件捧杉。在IOS中陕见,由響應(yīng)者鏈來對事件進(jìn)行響應(yīng),所有事件響應(yīng)的類都是UIResponder的子類味抖,響應(yīng)者鏈?zhǔn)且粋€(gè)由不同對象組成的層次結(jié)構(gòu)评甜,其中的每個(gè)對象將依次獲得響應(yīng)事件消息的機(jī)會(huì)。當(dāng)發(fā)生事件時(shí)非竿,事件首先被發(fā)送給第一響應(yīng)者蜕着,第一響應(yīng)者往往是事件發(fā)生的視圖,也就是用戶觸摸屏幕的地方红柱。事件將沿著響應(yīng)者鏈一直向下傳遞承匣,直到被接受并做出處理。一般來說锤悄,第一響應(yīng)者是個(gè)視圖對象或者其子類對象韧骗,當(dāng)其被觸摸后事件被交由它處理,如果它不處理零聚,事件就會(huì)被傳遞給它的視圖控制器對象viewcontroller(如果存在)袍暴,然后是它的父視圖(superview)對象(如果存在),以此類推隶症,直到頂層視圖政模。接下來會(huì)沿著頂層視圖(top view)到窗口(UIWindow對象)再到程序(UIApplication對象)。如果整個(gè)過程都沒有響應(yīng)這個(gè)事件蚂会,該事件就被丟棄淋样。一般情況下,在響應(yīng)者鏈中只要由對象處理事件胁住,事件就停止傳遞趁猴。

2刊咳、響應(yīng)者鏈(Responder Chain)

響應(yīng)者鏈有以下特點(diǎn):

1、響應(yīng)者鏈通常是由視圖(UIView)構(gòu)成的儡司;

2娱挨、一個(gè)視圖的下一個(gè)響應(yīng)者是它視圖控制器(UIViewController)(如果有的話),然后再轉(zhuǎn)給它的父視圖(Super View)捕犬;

3跷坝、視圖控制器(如果有的話)的下一個(gè)響應(yīng)者為其管理的視圖的父視圖;

4或听、單例的窗口(UIWindow)的內(nèi)容視圖將指向窗口本身作為它的下一個(gè)響應(yīng)者

需要指出的是探孝,Cocoa Touch應(yīng)用不像Cocoa應(yīng)用,它只有一個(gè)UIWindow對象誉裆,因此整個(gè)響應(yīng)者鏈要簡單一點(diǎn)顿颅;

5、單例的應(yīng)用(UIApplication)是一個(gè)響應(yīng)者鏈的終點(diǎn)足丢,它的下一個(gè)響應(yīng)者指向nil粱腻,以結(jié)束整個(gè)循環(huán)。

3斩跌、事件分發(fā)(Event Delivery)

第一響應(yīng)者(First responder)指的是當(dāng)前接受觸摸的響應(yīng)者對象(通常是一個(gè)UIView對象)绍些,即表示當(dāng)前該對象正在與用戶交互,它是響應(yīng)者鏈的開端耀鸦。整個(gè)響應(yīng)者鏈和事件分發(fā)的使命都是找出第一響應(yīng)者柬批。

UIWindow對象以消息的形式將事件發(fā)送給第一響應(yīng)者,使其有機(jī)會(huì)首先處理事件袖订。如果第一響應(yīng)者沒有進(jìn)行處理氮帐,系統(tǒng)就將事件(通過消息)傳遞給響應(yīng)者鏈中的下一個(gè)響應(yīng)者,看看它是否可以進(jìn)行處理洛姑。

iOS系統(tǒng)檢測到手指觸摸(Touch)操作時(shí)會(huì)將其打包成一個(gè)UIEvent對象上沐,并放入當(dāng)前活動(dòng)Application的事件隊(duì)列,單例的UIApplication會(huì)從事件隊(duì)列中取出觸摸事件并傳遞給單例的UIWindow來處理楞艾,UIWindow對象首先會(huì)使用hitTest:withEvent:方法尋找此次Touch操作初始點(diǎn)所在的視圖(View)参咙,即需要將觸摸事件傳遞給其處理的視圖,這個(gè)過程稱之為hit-test view硫眯。

UIWindow實(shí)例對象會(huì)首先在它的內(nèi)容視圖上調(diào)用hitTest:withEvent:蕴侧,此方法會(huì)在其視圖層級結(jié)構(gòu)中的每個(gè)視圖上調(diào)用pointInside:withEvent:(該方法用來判斷點(diǎn)擊事件發(fā)生的位置是否處于當(dāng)前視圖范圍內(nèi),以確定用戶是不是點(diǎn)擊了當(dāng)前視圖)两入,如果pointInside:withEvent:返回YES净宵,則繼續(xù)逐級調(diào)用,直到找到touch操作發(fā)生的位置,這個(gè)視圖也就是要找的hit-test view塘娶。
hitTest:withEvent:方法的處理流程如下:
首先調(diào)用當(dāng)前視圖的pointInside:withEvent:方法判斷觸摸點(diǎn)是否在當(dāng)前視圖內(nèi);
若返回NO,則hitTest:withEvent:返回nil;
若返回YES,則向當(dāng)前視圖的所有子視圖(subviews)發(fā)送hitTest:withEvent:消息痊夭,所有子視圖的遍歷順序是從最頂層視圖一直到到最底層視圖刁岸,即從subviews數(shù)組的末尾向前遍歷,直到有子視圖返回非空對象或者全部子視圖遍歷完畢她我;
若第一次有子視圖返回非空對象虹曙,則hitTest:withEvent:方法返回此對象,處理結(jié)束番舆;
如所有子視圖都返回非酝碳,則hitTest:withEvent:方法返回自身(self)。

4恨狈、說明

1疏哗、響應(yīng)者鏈的傳遞順序是從子類逐級到父類的傳遞方向,事件分發(fā)的順序是從父類逐級到子類的順序禾怠。

2返奉、如果最終hit-test沒有找到第一響應(yīng)者,或者第一響應(yīng)者沒有處理該事件吗氏,則該事件會(huì)沿著響應(yīng)者鏈向上回溯芽偏,如果UIWindow實(shí)例和UIApplication實(shí)例都不能處理該事件,則該事件會(huì)被丟棄弦讽;

3污尉、hitTest:withEvent:方法將會(huì)忽略隱藏(hidden=YES)的視圖,禁止用戶操作(userInteractionEnabled=YES)的視圖往产,以及alpha級別小于0.01(alpha<0.01)的視圖被碗。如果一個(gè)子視圖的區(qū)域超過父視圖的bound區(qū)域(父視圖的clipsToBounds 屬性為NO,這樣超過父視圖bound區(qū)域的子視圖內(nèi)容也會(huì)顯示)捂齐,那么正常情況下對子視圖在父視圖之外區(qū)域的觸摸操作不會(huì)被識(shí)別,因?yàn)楦敢晥D的pointInside:withEvent:方法會(huì)返回NO,這樣就不會(huì)繼續(xù)向下遍歷子視圖了蛮放。當(dāng)然,也可以重寫pointInside:withEvent:方法來處理這種情況奠宜。

4包颁、我們可以重寫hitTest:withEvent:來達(dá)到某些特定的目的,實(shí)際應(yīng)用中很少用到這些压真。

以上內(nèi)容參考作者原文地址:原文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娩嚼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子滴肿,更是在濱河造成了極大的恐慌岳悟,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贵少,居然都是意外死亡呵俏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門滔灶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來普碎,“玉大人,你說我怎么就攤上這事录平÷槌担” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵斗这,是天一觀的道長动猬。 經(jīng)常有香客問我,道長表箭,這世上最難降的妖魔是什么赁咙? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮免钻,結(jié)果婚禮上序目,老公的妹妹穿的比我還像新娘。我一直安慰自己伯襟,他們只是感情好猿涨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著姆怪,像睡著了一般叛赚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上稽揭,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天俺附,我揣著相機(jī)與錄音,去河邊找鬼溪掀。 笑死事镣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的揪胃。 我是一名探鬼主播璃哟,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼喊递!你這毒婦竟也來了随闪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤骚勘,失蹤者是張志新(化名)和其女友劉穎铐伴,沒想到半個(gè)月后撮奏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡当宴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年畜吊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片户矢。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡定拟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逗嫡,到底是詐尸還是另有隱情,我是刑警寧澤株依,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布驱证,位于F島的核電站,受9級特大地震影響恋腕,放射性物質(zhì)發(fā)生泄漏抹锄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一荠藤、第九天 我趴在偏房一處隱蔽的房頂上張望伙单。 院中可真熱鬧,春花似錦哈肖、人聲如沸吻育。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽布疼。三九已至,卻和暖如春币狠,著一層夾襖步出監(jiān)牢的瞬間游两,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工漩绵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贱案,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓止吐,卻偏偏與公主長得像宝踪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子碍扔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355