objc_msgSend消息流程之動態(tài)方法決議和消息轉(zhuǎn)發(fā)

在前兩篇文章objc_msgSend流程之快速查找objc_msgSend流程之慢速查找分析了objc_msgSend快速查找慢速查找,當(dāng)前面這兩種方式都沒找到對應(yīng)的方法實現(xiàn)時焊虏,我們可以通過操作下面兩個方法來避免方法未實現(xiàn)奔潰報錯

  • 動態(tài)方法決議:在慢速查找流程未找到后澎嚣,會執(zhí)行一次
  • 消息轉(zhuǎn)發(fā):如果動態(tài)方法決議沒有找到實現(xiàn)褥琐,則進行消息轉(zhuǎn)發(fā)
    • 快速轉(zhuǎn)發(fā)
    • 慢速轉(zhuǎn)發(fā)
實例方法報錯
實例方法報錯
類方法報錯
類方法報錯

方法未實現(xiàn)報錯源碼

匯編__objc_msgForward_impcache方法

STATIC_ENTRY __objc_msgForward_impcache

// No stret specialization.
b   __objc_msgForward

END_ENTRY __objc_msgForward_impcache

//??
ENTRY __objc_msgForward

adrp    x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17
    
END_ENTRY __objc_msgForward

在匯編實現(xiàn)中查找_objc_forward_handler方法

// Default forward handler halts the process.
__attribute__((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
    _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
                "(no message forward handler is installed)", 
                class_isMetaClass(object_getClass(self)) ? '+' : '-', 
                object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;

objc_defaultForwardHandler方法就是我們?nèi)粘i_發(fā)中常見錯誤沒有實現(xiàn)函數(shù)磕洪,運行程序,崩潰時報的錯誤提示

防止方法未實現(xiàn)崩潰的三次機會

  • 【第一次機會】動態(tài)方法決議
  • 消息轉(zhuǎn)發(fā)流程
    • 【第二次機會】快速轉(zhuǎn)發(fā)
    • 【第三次機會】慢速轉(zhuǎn)發(fā)

【第一次機會】動態(tài)方法決議

慢速查找流程未找到方法實現(xiàn)時谷异,首先嘗試一次動態(tài)方法決議,源碼實現(xiàn)如下

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();
    //對象 -- 類
    if (! cls->isMetaClass()) { //類不是元類尺上,調(diào)用對象的解析方法
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {//如果是元類,調(diào)用類的解析方法构资, 類 -- 元類
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        //為什么要有這行代碼? -- 類方法在元類中是對象方法唉窃,所以還是需要查詢元類中對象方法的動態(tài)方法決議
        if (!lookUpImpOrNil(inst, sel, cls)) { //如果沒有找到或者為空,在元類的對象方法解析方法中查找
            resolveInstanceMethod(inst, sel, cls);
        }
    }

    // chances are that calling the resolver have populated the cache
    // so attempt using it
    //如果方法解析中將其實現(xiàn)指向其他方法件已,則繼續(xù)走方法查找流程
    return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
  • 判斷是否是元類
    • 如果是,執(zhí)行實例方法的動態(tài)決議resolveInstanceMethod
    • 如果是元類,執(zhí)行類方法的動態(tài)方法決議resolveClassMethod铜秆,如果元類中沒有找到或者為,則在元類實例方法的動態(tài)方法決議resolveInstanceMethod中查找梅屉,是因為類方法在元類中是實例方法,所以還需要查找元類中的實例方法的動態(tài)決議
  • 如果動態(tài)方法決議中,將其實現(xiàn)指向了其他方法搓幌,則繼續(xù)查找指定的imp,即繼續(xù)慢速查找lookUpImpOrForward流程
    動態(tài)方法決議流程

實例方法的動態(tài)決議

實例方法的調(diào)用拐揭,在快速查找和慢速查找均未找到實例方法的實現(xiàn)時盟猖,我們還有一次挽救的機會值依,即嘗試動態(tài)方法決議颇蜡,由于是實例方法,所以會走到resolveInstanceMethod方法领迈,源碼如下:

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);
    
    // look的是 resolveInstanceMethod --相當(dāng)于是發(fā)送消息前的容錯處理
    if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel); //發(fā)送resolve_sel消息

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    //查找say666
    IMP imp = lookUpImpOrNil(inst, sel, cls);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}

主要分為以下幾個步驟

  • 發(fā)送resolveInstanceMethod 消息前尘喝,需要查找cls類中是否有改方法的實現(xiàn),即通過lookUpImpOrNil方法又會進入lookUpImpOrForward慢速查找流程查找resolveInstanceMethod方法
    • 如果沒有,直接返回
    • 如果有友题,則發(fā)送resolveInstanceMethod消息
  • 再次慢速查找實例方法的實現(xiàn)嗤堰,即通過lookUpImpOrNil方法又會進入lookUpImpOrForward慢速查找流程查找對應(yīng)的實例方法

奔潰修改

針對實例方法未實現(xiàn)的奔潰報錯,我們可以通過在重寫resolveInstanceMethod 類方法咆爽,并將其指向其他方法的實現(xiàn)梁棠,即將實例方法say666的實現(xiàn)指向sayMaster

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(say666)) {
        NSLog(@"%@ 來了", NSStringFromSelector(sel));
        //獲取sayMaster方法的imp
        IMP imp = class_getMethodImplementation(self, @selector(sayMaster));
        //獲取sayMaster的實例方法
        Method sayMethod  = class_getInstanceMethod(self, @selector(sayMaster));
        //獲取sayMaster的豐富簽名
        const char *type = method_getTypeEncoding(sayMethod);
        //將sel的實現(xiàn)指向sayMaster
        return class_addMethod(self, sel, imp, type);
    }
    
    return [super resolveInstanceMethod:sel];
}

重新運行,并打印堆棧信息


堆棧信息
  • 【第一次動態(tài)決議】查找say666方法時進入動態(tài)方法決議
  • 【第二次動態(tài)決議】在慢速轉(zhuǎn)發(fā)流程中調(diào)用了CoreFoundation框架中的NSObject(NSObject) methodSignatureForSelector:后斗埂,會再次進入動態(tài)方法決議

第二次動態(tài)決議流程分析請看文末的問題探索

類方法的動態(tài)決議

針對類方法的重寫resolveClassMethod,需要注意傳入的cls不再是,而是元類实夹,因為類方法在元類中是實例方法,可以通過objc_getMetaClass方法獲取元類

優(yōu)化
  • 實例方法:類 -- 父類 -- 根類 -- nil
  • 類方法:元類 -- 根元類 -- 根類 -- nil
    通過上面方法的查找路徑可以發(fā)現(xiàn)沦辙,都會來到根類(NSObject)中查找
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(say666)) {
        NSLog(@"%@ 來了", NSStringFromSelector(sel));
        
        IMP imp = class_getMethodImplementation(self, @selector(sayMaster));
        Method sayMethod  = class_getInstanceMethod(self, @selector(sayMaster));
        const char *type = method_getTypeEncoding(sayMethod);
        return class_addMethod(self, sel, imp, type);
    }else if (sel == @selector(sayNB)) {
        NSLog(@"%@ 來了", NSStringFromSelector(sel));
        
        IMP imp = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        Method lgClassMethod  = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        const char *type = method_getTypeEncoding(lgClassMethod);
        return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
    }
    return NO;
}

上面這種寫法會導(dǎo)致系統(tǒng)方法也會被更改狞玛,針對這一點戴已,我們可以通過自定義類中方法的統(tǒng)一方法名前綴拴疤,根據(jù)前綴來判斷是否是自定義方法荞膘,然后統(tǒng)一處理自定義方法狭郑,例如可以在崩潰前pop到首頁黄绩,主要是用于app線上防崩潰的處理袒餐,提升用戶的體驗

消息轉(zhuǎn)發(fā)流程

在源碼中找不到消息轉(zhuǎn)發(fā)的源碼匕积,但是我們可以通過下面方式來了解,方法調(diào)用奔潰前都走了那些方法

  • 通過instrumentObjcMessageSends方式打印發(fā)送消息的日志
  • 痛過hopper/IDA反編譯

通過instrumentObjcMessageSends

  • 通過lookUpImpOrForward --> log_and_fill_cache --> logMessageSend精居,在logMessageSend源碼中找到instrumentObjcMessageSends的源碼實現(xiàn)沃但,然后再main函數(shù)中調(diào)用instrumentObjcMessageSends打印方法調(diào)用的日志信息佛吓,
    - 1灼伤、打開objcMsgLogEnabled 開關(guān),即調(diào)用instrumentObjcMessageSends方法時,傳入YES
    - 2可柿、在main中通過extern聲明instrumentObjcMessageSends方法
extern void instrumentObjcMessageSends(BOOL flag);

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        LGPerson *person = [LGPerson alloc];
        instrumentObjcMessageSends(YES);
        [person sayHello];
        instrumentObjcMessageSends(NO);
        NSLog(@"Hello, World!");
    }
    return 0;
}
  • 通過logMessageSend源碼鸠踪,可以發(fā)現(xiàn)消息發(fā)送打印信息存儲在/tmp/msgSends目錄

    消息發(fā)送日志路徑

  • 運行代碼,并前往/tmp/msgSends目錄复斥,發(fā)現(xiàn)有msgSends開頭的日志文件营密,可以發(fā)現(xiàn)在奔潰前,執(zhí)行了以下方法

    • 兩次動態(tài)方法決議resolveInstanceMethod 方法
    • 兩次消息快速轉(zhuǎn)發(fā)forwardingTargetForSelector 方法
    • 兩次消息慢速轉(zhuǎn)發(fā)methodSignatureForSelector + resolveInvocation
      消息發(fā)送日志詳情

通過hopper/IDA反編譯

Hopper和IDA是一個可以幫助我們靜態(tài)分析可視性文件的工具目锭,可以將可執(zhí)行文件反匯編成偽代碼评汰,控制流程圖等纷捞,下面以Hopper為例

  • 運行程序奔潰,查看堆棧信息


    查看堆棧打印信息
  • 發(fā)現(xiàn)___forwarding___ 來自CoreFoundation框架

    ___forwarding___源碼定位

  • 通過image list命令被去,讀取整個鏡像文件主儡,然后搜索CoreFoundation,可以查看其可執(zhí)行文件的路徑

    查找CoreFoundation

  • 通過文件路徑编振,找到CoreFoundation的執(zhí)行文件

    CoreFoundation執(zhí)行文件路徑

  • 打開hopper缀辩,選擇Try the Demo,然后將CoreFoundation的可執(zhí)行文件拖入hopper進行反編譯踪央,選擇x86(64 bits)

    hopper反編譯

    hoperr反編譯

  • 反匯編后的界面


    hopper界面
  • 通過左側(cè)的搜索框搜索__forwarding_prep_0___后臀玄,選擇偽代碼

    • 以下是__forwarding_prep_0___的匯編偽代碼,跳轉(zhuǎn)至___forwarding___

      偽代碼___forwarding___

    • 以下是___forwarding___的偽代碼實現(xiàn)畅蹂,首先查看是否實現(xiàn)forwardingTargetForSelector方法健无,如果沒有響應(yīng),跳轉(zhuǎn)至loc_6459b即快速轉(zhuǎn)發(fā)沒有響應(yīng)液斜,進入慢速轉(zhuǎn)發(fā)流程

      偽代碼-forwardingTargetForSelector

    • 跳轉(zhuǎn)至loc_6459b累贤,在其下方判斷是否響應(yīng)methodSignatureForSelector方法

      偽代碼-methodSignatureForSelector

    • 如果沒有響應(yīng),跳轉(zhuǎn)至loc_6490b少漆,則直接報錯

    • 如果獲取methodSignatureForSelector方法簽名為nil臼膏,也是直接報錯

      偽代碼-methodSignatureForSelector為nil時報錯

  • 如果methodSignatureForSelector返回值不為空,則在forwardInvocation方法中對invocation進行處理

    偽代碼-forwardInvocation

通過上面兩種查找方式可以驗證示损,消息轉(zhuǎn)發(fā)的方法有3個

  • 【快速轉(zhuǎn)發(fā)】forwardingTargetForSelector

  • 【慢速轉(zhuǎn)發(fā)】
    - methodSignatureForSelector
    - forwardInvocation
    所以消息轉(zhuǎn)發(fā)的整體流程圖如下

    消息轉(zhuǎn)發(fā)流程圖

  • 【快速轉(zhuǎn)發(fā)】當(dāng)慢速查找以及動態(tài)方法決議都沒找到渗磅,首先進行快速消息轉(zhuǎn)發(fā)forwardingTargetForSelector方法
    - 如果返回消息接收者检访,在消息接收者中還是沒有找到始鱼,則進入另一個方法的查找流程
    - 如果返回nil,則進入慢速消息轉(zhuǎn)發(fā)
    -【慢速轉(zhuǎn)發(fā)】 執(zhí)行到methodSignatureForSelector
    - 如果返回的方法簽名nil脆贵,則直接崩潰報錯
    - 如果返回的方法簽名不為nil医清,走到forwardInvocation方法中,對invocation事務(wù)進行處理卖氨,不處理也不會報錯

【第二次機會】快速轉(zhuǎn)發(fā)

在LGPerson中重寫forwardingTargetForSelector方法会烙,將LGPerson的實例方法的接收者指定為LGStudent的對象(LGStudent類中有say666的具體實現(xiàn)),如下所示

- (id)forwardingTargetForSelector:(SEL)aSelector{
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));

//     runtime + aSelector + addMethod + imp
    //將消息的接收者指定為LGStudent双泪,在LGStudent中查找say666的實現(xiàn)
    return [LGStudent alloc];
}

也可以直接不指定消息接收者持搜,直接調(diào)用父類的該方法,如果還是沒有找到焙矛,則直接報錯

- (id)forwardingTargetForSelector:(SEL)aSelector{
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));

    // runtime + aSelector + addMethod + imp
    return [super forwardingTargetForSelector:aSelector];
}

【第三次機會】慢速轉(zhuǎn)發(fā)

如果消息快速轉(zhuǎn)發(fā)還是沒有找到葫盼,則還有最后一次機會,即在LGPerson中重寫methodSignatureForSelector

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"%s - %@",__func__,anInvocation);
}

也可以處理invocation事務(wù)

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"%s - %@",__func__,anInvocation);
    anInvocation.target = [LGStudent alloc];
    [anInvocation invoke];
}

我們在forwardInvocation方法中處理和不處理invocation事務(wù)村斟,都不會報錯

“動態(tài)方法決議為什么執(zhí)行兩次贫导?” 探索

在慢速查找流程中抛猫,我們了解到resolveInstanceMethod方法的執(zhí)行是通過lookUpImpOrForward --> resolveMethod_locked --> resolveInstanceMethod來到resolveInstanceMethod源碼,在源碼中通過發(fā)送resolve_sel消息觸發(fā)

resolveInstanceMethod方法觸發(fā)原理

  • 可以在resolveInstanceMethod方法中IMP imp = lookUpImpOrNil(inst, sel, cls);處加一個斷點孩灯,通過bt打印堆棧信息來看到底發(fā)生了什么

    第一次動態(tài)方法決議堆棧信息

  • 繼續(xù)往下執(zhí)行闺金,直到第二次“來了”打印,查看堆棧信息峰档,在第二次中败匹,我們可以看到是通過CoreFoundation的-[NSObject(NSObject) methodSignatureForSelector:]方法,然后通過`class_getInstanceMethod再次進入動態(tài)方法決議

    第二次動態(tài)方法決議堆棧信息

  • 通過Hopper反匯編CoreFoundation的可執(zhí)行文件讥巡,查看methodSignatureForSelector方法的偽代碼

    methodSignatureForSelector偽代碼進入方式

  • 通過methodSignatureForSelector偽代碼進入___methodDescriptionForSelector的實現(xiàn)

    methodDescriptionForSelector方法的偽代碼

  • 進入___methodDescriptionForSelector的偽代碼實現(xiàn)掀亩,結(jié)合匯編的堆棧打印,可以看到在___methodDescriptionForSelector這個方法中調(diào)用了objc4-781class_getInstanceMethod

    ___methodDescriptionForSelector方法的偽代碼調(diào)用了class_getInstanceMethod

  • 在objc中的源碼中搜索class_getInstanceMethod欢顷,其源碼實現(xiàn)如下所示

    class_getInstanceMethod方法源碼

如下所示槽棍,在class_getInstanceMethod方法處加一個斷點,在執(zhí)行了methodSignatureForSelector方法后抬驴,返回了簽名炼七,說明方法簽名是生效的,蘋果在走到invocation之前布持,給了開發(fā)者一次機會再去查詢豌拙,所以走到class_getInstanceMethod這里,又去走了一遍方法查詢say666,然后會再次走到動態(tài)方法決議

class_getInstanceMethod方法調(diào)試驗證

所以题暖,上述的分析也印證了前文中resolveInstanceMethod方法執(zhí)行了兩次的原因

通過代碼來推導(dǎo)
  • LGPerson中重寫resolveInstanceMethod方法姆蘸,并加上class_addMethod操作即賦值IMP,此時resolveInstanceMethod會走兩次嗎

    resolveInstanceMethod方法調(diào)試驗證

    【結(jié)論】:通過運行發(fā)現(xiàn)芙委,如果賦值了IMP,動態(tài)方法決議只會走一次狂秦,說明不是在這里走第二次動態(tài)方法決議灌侣,

  • 去掉resolveInstanceMethod方法中的賦值IMP,在LGPerson類中重寫forwardingTargetForSelector方法裂问,并指定返回值為[LGStudent alloc]侧啼,重新運行,如果resolveInstanceMethod打印了兩次堪簿,說明是在forwardingTargetForSelector方法之前執(zhí)行了 動態(tài)方法決議痊乾,反之,在forwardingTargetForSelector方法之后

    forwardingTargetForSelector方法調(diào)試驗證

【結(jié)論】:發(fā)現(xiàn)resolveInstanceMethod中的打印還是只打印了一次椭更,數(shù)排名第二次動態(tài)方法決議 在forwardingTargetForSelector方法后

  • 在LGPerson中重寫methodSignatureForSelectorforwardInvocation哪审,運行
    methodSignatureForSelector+forwardInvocation方法調(diào)試驗證

    【結(jié)論】:第二次動態(tài)方法決議methodSignatureForSelectorforwardInvocation方法之間

經(jīng)過上面的論證,我們了解到其實在慢速轉(zhuǎn)發(fā)流程中虑瀑,在methodSignatureForSelectorforwardInvocation方法之間還有一次動態(tài)方法決議

消息轉(zhuǎn)發(fā)流程-2

總結(jié)

objc_msgSend發(fā)送消息的流程

  • 【快速查找流程】首先湿滓,在類的緩存cache中查找指定方法的實現(xiàn)

  • 【慢速查找流程】如果緩存中沒有找到滴须,則在類的方法列表中查找,如果還是沒找到叽奥,則去父類鏈的緩存和方法列表中查找

  • 【動態(tài)方法決議】如果慢速查找還是沒有找到時扔水,第一次補救機會就是嘗試一次動態(tài)方法決議,即重寫resolveInstanceMethod/resolveClassMethod 方法

  • 【消息轉(zhuǎn)發(fā)】如果動態(tài)方法決議還是沒有找到朝氓,則進行消息轉(zhuǎn)發(fā)魔市,消息轉(zhuǎn)發(fā)中有兩次補救機會:快速轉(zhuǎn)發(fā)+慢速轉(zhuǎn)發(fā)

  • 如果轉(zhuǎn)發(fā)之后也沒有,則程序直接報錯崩潰unrecognized selector sent to instance

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赵哲,一起剝皮案震驚了整個濱河市待德,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌誓竿,老刑警劉巖磅网,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異筷屡,居然都是意外死亡涧偷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門毙死,熙熙樓的掌柜王于貴愁眉苦臉地迎上來燎潮,“玉大人,你說我怎么就攤上這事扼倘∪贩猓” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵再菊,是天一觀的道長爪喘。 經(jīng)常有香客問我,道長纠拔,這世上最難降的妖魔是什么秉剑? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮稠诲,結(jié)果婚禮上侦鹏,老公的妹妹穿的比我還像新娘。我一直安慰自己臀叙,他們只是感情好略水,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著劝萤,像睡著了一般渊涝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天驶赏,我揣著相機與錄音炸卑,去河邊找鬼。 笑死煤傍,一個胖子當(dāng)著我的面吹牛盖文,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚯姆,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼五续,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了龄恋?” 一聲冷哼從身側(cè)響起就斤,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤矮燎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庄岖,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡绢记,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年柳洋,在試婚紗的時候發(fā)現(xiàn)自己被綠了叔汁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡乘碑,死狀恐怖挖息,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兽肤,我是刑警寧澤套腹,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站资铡,受9級特大地震影響电禀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笤休,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一鞭呕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宛官,春花似錦、人聲如沸瓦糕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咕娄。三九已至亥揖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背费变。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工摧扇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挚歧。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓扛稽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親滑负。 傳聞我的和親對象是個殘疾皇子在张,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

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