iOS-底層(10):objc_msgSend流程分析之慢速查找與消息轉(zhuǎn)發(fā)

objc_msgSend 慢速查找流程分析

前一篇我們分析了匯編快速查找,如果沒(méi)有找到,就會(huì)進(jìn)入CheckMiss或者JumpMiss

.macro CheckMiss
    // miss if bucket->sel == 0
.if $0 == GETIMP
    cbz p9, LGetImpMiss
.elseif $0 == NORMAL
    cbz p9, __objc_msgSend_uncached
.elseif $0 == LOOKUP
    cbz p9, __objc_msgLookup_uncached
.else
.abort oops
.endif
.endmacro

.macro JumpMiss
.if $0 == GETIMP
    b   LGetImpMiss
.elseif $0 == NORMAL
    b   __objc_msgSend_uncached
.elseif $0 == LOOKUP
    b   __objc_msgLookup_uncached
.else
.abort oops
.endif
.endmacro

然后進(jìn)入到__objc_msgSend_uncached

STATIC_ENTRY __objc_msgSend_uncached
    UNWIND __objc_msgSend_uncached, FrameWithNoSaves

    // THIS IS NOT A CALLABLE C FUNCTION
    // Out-of-band p16 is the class to search
    
    MethodTableLookup //方法表中查找
    TailCallFunctionPointer x17

    END_ENTRY __objc_msgSend_uncached

MethodTableLookup

.macro MethodTableLookup
    
    // push frame
    SignLR
    stp fp, lr, [sp, #-16]!
    mov fp, sp

    // save parameter registers: x0..x8, q0..q7
    sub sp, sp, #(10*8 + 8*16)
    stp q0, q1, [sp, #(0*16)]
    stp q2, q3, [sp, #(2*16)]
    stp q4, q5, [sp, #(4*16)]
    stp q6, q7, [sp, #(6*16)]
    stp x0, x1, [sp, #(8*16+0*8)]
    stp x2, x3, [sp, #(8*16+2*8)]
    stp x4, x5, [sp, #(8*16+4*8)]
    stp x6, x7, [sp, #(8*16+6*8)]
    str x8,     [sp, #(8*16+8*8)]

    // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
    // receiver and selector already in x0 and x1
    mov x2, x16
    mov x3, #3
    bl  _lookUpImpOrForward//跳轉(zhuǎn)到_lookUpImpOrForward

    // IMP in x0
    mov x17, x0
    
    // restore registers and return
    ldp q0, q1, [sp, #(0*16)]
    ldp q2, q3, [sp, #(2*16)]
    ldp q4, q5, [sp, #(4*16)]
    ldp q6, q7, [sp, #(6*16)]
    ldp x0, x1, [sp, #(8*16+0*8)]
    ldp x2, x3, [sp, #(8*16+2*8)]
    ldp x4, x5, [sp, #(8*16+4*8)]
    ldp x6, x7, [sp, #(8*16+6*8)]
    ldr x8,     [sp, #(8*16+8*8)]

    mov sp, fp
    ldp fp, lr, [sp], #16
    AuthenticateLR

imp找不到會(huì)跳轉(zhuǎn)到_lookUpImpOrForward, _lookUpImpOrForward沒(méi)有.macro宏爸业,說(shuō)明跳轉(zhuǎn)到c或者c++的代碼中果覆。我們可以通過(guò)匯編調(diào)試驗(yàn)證一下宰闰,添加斷點(diǎn)存捺,點(diǎn)擊control + stepinto

打開(kāi)debug--> Debug WorkFlow --> always show disassembly

image.png

斷住objc_msgSend贼邓,繼續(xù)control + stepInto, 斷住_objc_msgSend_uncached,繼續(xù)control + stepInto

image.png

最后走到的是lookUpImpOrForward热鞍,這里并不是匯編實(shí)現(xiàn)葫慎,而是C/C++實(shí)現(xiàn)

  • C/C++中調(diào)用匯編,在匯編的定義中要加一個(gè) _ 下劃線
  • 匯編中調(diào)用C/C++薇宠,C/C++的函數(shù)定義中要減一個(gè) _ 下劃線

慢速查找的C/C++部分

全局搜索lookUpImpOrForward,在最新的objc-runtime-new.mm找到偷办,是一個(gè)C函數(shù),我們來(lái)看代碼昼接,我加了相應(yīng)的注釋

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;
    Class curClass;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup
    //在多線程情況下方法可能會(huì)緩存
    if (fastpath(behavior & LOOKUP_CACHE)) {
        //通過(guò)匯編獲取imp
        imp = cache_getImp(cls, sel);
        if (imp) goto done_nolock;
    }

    //線程加鎖
    runtimeLock.lock();

    //檢查是否是被認(rèn)可的對(duì)象爽篷,已知類(或者是內(nèi)置到二進(jìn)制文件中,或者合法注冊(cè)通過(guò))
    checkIsKnownClass(cls);
    //如果類沒(méi)有實(shí)現(xiàn)
    if (slowpath(!cls->isRealized())) {
        //實(shí)現(xiàn)類,內(nèi)部父類繼承鏈向上依次實(shí)現(xiàn)慢睡。確保后面的方法查找可以進(jìn)行逐工。
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        // runtimeLock may have been dropped but is now locked again
    }

    //判斷類是否初始化,如果沒(méi)有漂辐,需要先初始化
    if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
    }

    runtimeLock.assertLocked();
    curClass = cls;

    //*unreasonableClassCount為類的任何迭代提供一個(gè)上限泪喊,在運(yùn)行時(shí)元數(shù)據(jù)被破壞時(shí)防止自旋。
    //這是個(gè)無(wú)限循環(huán)髓涯,要使用break或goto來(lái)跳出循環(huán)
    for (unsigned attempts = unreasonableClassCount();;) {
        // curClass method list.
       // 本類進(jìn)行一次imp查找
        Method meth = getMethodNoSuper_nolock(curClass, sel);
        if (meth) {
            imp = meth->imp;
            goto done;
        }

        // curClass向繼承鏈傳遞賦值袒啼,當(dāng)找到nil的時(shí)候讓 imp = forward_imp, break,終止循環(huán)
        if (slowpath((curClass = curClass->superclass) == nil)) {
            // No implementation found, and method resolver didn't help.
            // Use forwarding.
            imp = forward_imp;
            break;
        }

        // Halt if there is a cycle in the superclass chain.
       // 如果父類鏈存在循環(huán),則報(bào)錯(cuò)蚓再。
        if (slowpath(--attempts == 0)) {
            _objc_fatal("Memory corruption in class list.");
        }

        // Superclass cache.
        //匯編查找父類緩存
        imp = cache_getImp(curClass, sel); 
        // 當(dāng)imp == forward_imp 
        if (slowpath(imp == forward_imp)) {
            // Found a forward:: entry in a superclass.
            // Stop searching, but don't cache yet; call method
            // resolver for this class first.
            break;
        }
        // 如果在父類中找到了滑肉,直接 goto done
        if (fastpath(imp)) {
            // Found the method in a superclass. Cache it in this class.
            goto done;
        }
    }

    // No implementation found. Try method resolver once.
    //這個(gè)& ^= 這個(gè)算法是為了讓這段代碼只執(zhí)行一次
    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
  // 進(jìn)入動(dòng)態(tài)方法決議
        return resolveMethod_locked(inst, sel, cls, behavior);
    }

 done:
    log_and_fill_cache(cls, imp, sel, inst, curClass);
    runtimeLock.unlock();
 done_nolock:
    if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
        return nil;
    }
    return imp;
}

總結(jié)一下上述過(guò)程:

  1. 找一下緩存,排除多線程影響
  2. 判斷類是否被認(rèn)可的類摘仅,已知類
  3. 判斷類是否已實(shí)現(xiàn)
  4. 判斷類是否已初始化
  5. 進(jìn)入for死循環(huán)靶庙,先進(jìn)行一次本類的方法查找(二分查找),讓臨時(shí)類curClass指向父類娃属,通過(guò)匯編依次向上查找(cache_getImp)六荒,直到curClass找到nil ,將imp賦值為_(kāi)objc_msgForward_impcache矾端,然后break跳出循環(huán)掏击。
  6. 進(jìn)行一次動(dòng)態(tài)方法決議,resolveMethod_locked 判斷秩铆,resolveInstanceMethod和resolveInstanceMethod砚亭,是否有做處理,如果處理了豺旬,重新找一遍imp,若果沒(méi)有處理钠惩,繼續(xù)lookUpImpOrForward柒凉。
  7. 消息快速轉(zhuǎn)發(fā),
  8. 消息的慢速轉(zhuǎn)發(fā)

上面是結(jié)合代碼的描述過(guò)程族阅,我只描述了關(guān)鍵部分,接下來(lái)我們來(lái)看一下流程圖

2251862-8f3c817f232e953b.png

我們來(lái)看看二分查找算法的實(shí)現(xiàn):

ALWAYS_INLINE static method_t *
findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
    ASSERT(list);

    const method_t * const first = &list->first;
    const method_t *base = first;
    const method_t *probe;
    uintptr_t keyValue = (uintptr_t)key;
    uint32_t count;
    
    for (count = list->count; count != 0; count >>= 1) {
        probe = base + (count >> 1);
        
        uintptr_t probeValue = (uintptr_t)probe->name;
        
        if (keyValue == probeValue) {
            //如果分類有相同方法膝捞,取分類的方法
            while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
                probe--;
            }
            return (method_t *)probe;
        }
        
        if (keyValue > probeValue) {
            base = probe + 1;
            count--;
        }
    }
    
    return nil;
}

二分查找是一種非常高效的查找算法坦刀,它的時(shí)間復(fù)雜度是O(logn),O(logn) 這種對(duì)數(shù)時(shí)間復(fù)雜度蔬咬。這是一種極其高效的時(shí)間復(fù)雜度鲤遥,因?yàn)?logn 是一個(gè)非常“恐怖”的數(shù)量級(jí)林艘,即便 n 非常非常大盖奈,對(duì)應(yīng)的 logn 也很小。比如 n 等于 2 的 32 次方狐援,這個(gè)數(shù)很大了吧钢坦?大約是 42 億。也就是說(shuō)啥酱,如果我們?cè)?42 億個(gè)數(shù)據(jù)中用二分查找一個(gè)數(shù)據(jù)爹凹,最多需要比較 32 次。

我們?cè)賮?lái)看看動(dòng)態(tài)方法決議

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());
    // 給你一次機(jī)會(huì),添加imp
    runtimeLock.unlock();
    //當(dāng)前類不是元類
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
   //當(dāng)前類是元類
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNil(inst, sel, cls)) {
        // 類方法在元類里就是實(shí)例方法镶殷,我們無(wú)法在元類里面寫(xiě)方法來(lái)處理禾酱,但是在NSObject里面是可以處理的,也就是實(shí)例方法
            resolveInstanceMethod(inst, sel, cls);
        }
    }

    // chances are that calling the resolver have populated the cache
    // so attempt using it
    return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}

resolveInstanceMethod

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
        // Resolver not implemented.
        // resolve_sel 沒(méi)有實(shí)現(xiàn)返回
        return;
    }
    //發(fā)送一次resolve_sel消息,也就是調(diào)用一次resolve_sel方法
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    // 再找一次sel的imp;
    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));
        }
    }
}

resolveClassMethod

static void resolveClassMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    ASSERT(cls->isMetaClass());

    if (!lookUpImpOrNil(inst, @selector(resolveClassMethod:), cls)) {
        // Resolver not implemented.
        return;
    }

    Class nonmeta;
    {
        mutex_locker_t lock(runtimeLock);
        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        // +initialize path should have realized nonmeta already
        if (!nonmeta->isRealized()) {
            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                        nonmeta->nameForLogging(), nonmeta);
        }
    }
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveClassMethod adds to self->ISA() a.k.a. cls
    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 resolveClassMethod:%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));
        }
    }
}

首先判斷resolveInstanceMethod是否實(shí)現(xiàn)颤陶,沒(méi)有時(shí)間直接返回颗管,如果有實(shí)現(xiàn),發(fā)送消息resolveInstanceMethod滓走,即調(diào)用我們自己處理的resolveInstanceMethod方法忙上,然后進(jìn)入慢速查找lookUpImpOrNil查找IMP,這里找到IMP只是為了打印消息闲坎,然后再進(jìn)入lookUpImpOrNil查找IMP 返回IMP

動(dòng)態(tài)方法決議的處理

#import "LGPerson.h"
#import <objc/message.h>

@implementation LGPerson
- (void)sayHello{
    NSLog(@"%s",__func__);
}

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


+ (void)lgClassMethod{
    NSLog(@"%s",__func__);
}


+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"%@ 來(lái)了",NSStringFromSelector(sel));
    if (sel == @selector(say666)) {
        NSLog(@"%@ 來(lái)了",NSStringFromSelector(sel));

        IMP imp           = class_getMethodImplementation(self, @selector(sayMaster));
        Method sayMMethod = class_getInstanceMethod(self, @selector(sayMaster));
        const char *type  = method_getTypeEncoding(sayMMethod);
        return class_addMethod(self, sel, imp, type);
    }
   
    return [super resolveInstanceMethod:sel];
}

+ (BOOL)resolveClassMethod:(SEL)sel{
    NSLog(@"%@ 來(lái)了",NSStringFromSelector(sel));
    if (sel == @selector(sayNB)) {
         //注意這里類方法要添加到元類里面
        IMP imp           = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        Method sayMMethod = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        const char *type  = method_getTypeEncoding(sayMMethod);
        return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
    }
    return [super resolveClassMethod:sel];
}

在類方法查找imp的過(guò)程中疫粥,最終找到NSObject,那么我們想可以統(tǒng)一將動(dòng)態(tài)方法決議寫(xiě)到NSObject的分類中

implementation NSObject (LG)

// 調(diào)用方法的時(shí)候 - 分類

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    

    NSLog(@"%@ 來(lái)了",NSStringFromSelector(sel));
    if (sel == @selector(say666)) {
        NSLog(@"%@ 來(lái)了",NSStringFromSelector(sel));

        IMP imp           = class_getMethodImplementation(self, @selector(sayMaster));
        Method sayMMethod = class_getInstanceMethod(self, @selector(sayMaster));
        const char *type  = method_getTypeEncoding(sayMMethod);
        return class_addMethod(self, sel, imp, type);
    }
    else if (sel == @selector(sayNB)) {
        
        IMP imp           = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        Method sayMMethod = c lass_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        const char *type  = method_getTypeEncoding(sayMMethod);
        return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
    }
    return NO;
}

/**
 
 1: 分類 - 便利
 2: 方法 - lg_model_tracffic
        - lg - model home - 奔潰 - pop Home
        - lg - mine  - mine
    切面 - SDK - 上傳
 3: AOP - 封裝SDK - 不處理
 4: 消息轉(zhuǎn)發(fā) - 
 
 */

@end

一般我們不在這一步做處理腰懂,因?yàn)橛锌赡鼙蛔宇悢r截梗逮。我們繼續(xù)探索動(dòng)態(tài)方法決議之后還走了哪些

在源碼中有打印消息發(fā)送的開(kāi)關(guān),我們?cè)贠C中使用要用extern 開(kāi)放出來(lái)

extern void instrumentObjcMessageSends(BOOL flag);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        instrumentObjcMessageSends(YES);
        LGPerson *person = [LGPerson alloc];
        [person sayHello];
        instrumentObjcMessageSends(YES);
        NSLog(@"Hello, World!");
    }
    return 0;
}

通過(guò)logMessageSend源碼绣溜,消息發(fā)送打印信息存儲(chǔ)在/tmp/msgSends/ 目錄慷彤,如下所示我們找到打印文件:

image.png

  • 發(fā)送了兩次動(dòng)態(tài)方法決議:resolveInstanceMethod方法
  • 發(fā)送了兩次消息快速轉(zhuǎn)發(fā):forwardingTargetForSelector方法
  • 發(fā)送了兩次消息慢速轉(zhuǎn)發(fā):methodSignatureForSelector + resolveInstanceMethod

我們?cè)谕ㄟ^(guò)bt命令看一下調(diào)用堆棧


image.png

我們發(fā)現(xiàn)___forwardingTarget___CoreFoundation框架中,但是這個(gè)框架蘋(píng)果并沒(méi)有開(kāi)源怖喻,我們可以通過(guò)image list底哗,讀取整個(gè)鏡像文件,然后搜索CoreFoundation,查看其可執(zhí)行文件的路徑锚沸,找到文件跋选,通過(guò)hopper進(jìn)行反匯編

image.png
image.png

image.png
image.png

判斷是否可以響應(yīng),發(fā)送消息看是否有接收者哗蜈。

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

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


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

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"%s - %@",__func__,anInvocation);
    // GM  sayHello - anInvocation - 漂流瓶 - anInvocation
    anInvocation.target = [LGStudent alloc];
    // anInvocation 保存 - 方法
    [anInvocation invoke];
}
2251862-71932fb077753303.jpg
image.png

我們可以看到 匯編又調(diào)用了一次動(dòng)態(tài)方法決議前标,這也是為什么resolveInstanceMethod走兩遍的原因。

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

未命名文件.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末距潘,一起剝皮案震驚了整個(gè)濱河市炼列,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌音比,老刑警劉巖俭尖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異洞翩,居然都是意外死亡稽犁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)菱农,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缭付,“玉大人,你說(shuō)我怎么就攤上這事循未∠菝ǎ” “怎么了秫舌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)绣檬。 經(jīng)常有香客問(wèn)我足陨,道長(zhǎng),這世上最難降的妖魔是什么娇未? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任墨缘,我火速辦了婚禮,結(jié)果婚禮上零抬,老公的妹妹穿的比我還像新娘镊讼。我一直安慰自己,他們只是感情好平夜,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布蝶棋。 她就那樣靜靜地躺著,像睡著了一般忽妒。 火紅的嫁衣襯著肌膚如雪玩裙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天段直,我揣著相機(jī)與錄音吃溅,去河邊找鬼。 笑死鸯檬,一個(gè)胖子當(dāng)著我的面吹牛决侈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播京闰,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼颜及,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蹂楣?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤讯蒲,失蹤者是張志新(化名)和其女友劉穎痊土,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體墨林,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赁酝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旭等。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酌呆。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖搔耕,靈堂內(nèi)的尸體忽然破棺而出隙袁,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布菩收,位于F島的核電站梨睁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏娜饵。R本人自食惡果不足惜坡贺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望箱舞。 院中可真熱鬧遍坟,春花似錦、人聲如沸晴股。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)队魏。三九已至公般,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胡桨,已是汗流浹背官帘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昧谊,地道東北人刽虹。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像呢诬,于是被迫代替她去往敵國(guó)和親涌哲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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