Runtime(二)-objc_msgSend

一、objc_msgSend

  • OC中的方法調(diào)用,其實都是轉(zhuǎn)換為objc_msgSend函數(shù)的調(diào)用
  • objc_msgSend的執(zhí)行流程可以分為3大階段
    • a)消息發(fā)送
    • b)動態(tài)方法解析
    • c)消息轉(zhuǎn)發(fā)

二、消息發(fā)送

  • Objective-C對象的分類中詳細(xì)的說了instance對象、class對象、meta-class對象。也詳細(xì)的說消息發(fā)送和方法尋找的過程
    objc_msgSend消息發(fā)送圖盜圖.png
  • Runtime(一)中又進一步說明了Class的結(jié)構(gòu),更加詳細(xì)的說明了Class中方法存儲的過程和尋找過程,先要從緩存中尋找是否有方法,然后再從方法列表中尋找方法,這個整個過程可以理解為消息發(fā)送,也就是整個OC執(zhí)行方法過程中的第一步

1、通過源碼來窺探消息發(fā)送原理

  • 1、查看源碼方式
在終端輸入下面的命令把OC代碼的.m文件轉(zhuǎn)換成.cpp文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc [OC文件] -o [.cpp文件]
  • 2、測試代碼
  • 2.1:RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

- (void)personInstanceMethod;

@end

#import "RevanPerson.h"

@implementation RevanPerson

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

@end
  • 2.2:測試代碼
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    RevanPerson *person = [[RevanPerson alloc] init];
    
    [person personInstanceMethod];
}

@end
打印輸出:
2018-07-12 14:18:31.450980+0800 01-Runtime_objc_msgSend[2057:54187] -[RevanPerson personInstanceMethod]
  • 3、測試代碼源碼
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));

    RevanPerson *person = ((RevanPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((RevanPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("RevanPerson"), sel_registerName("alloc")), sel_registerName("init"));

    ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("personInstanceMethod"));
}

//簡化后源碼
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
    //實例化person對象
    RevanPerson *person =
    objc_msgSend(
                 objc_msgSend(objc_getClass("RevanPerson"), sel_registerName("alloc")),
                 sel_registerName("init")
                 );
    //person對象調(diào)用personInstanceMethod方法
    objc_msgSend(
                 person, sel_registerName("personInstanceMethod")
                 );
}
  • 源碼解析
    • 1、在實例化person對象時使用了2次objc_msgSend發(fā)送消息函數(shù);里面的一次是給RevanPerson類發(fā)送一個alloc消息;外面的一次是給RevanPerson的instance對象發(fā)送一個init消息
    • 2、最后給person對象發(fā)送一個personInstanceMethod消息

2、當(dāng)找不到personInstanceMethod方法會怎么樣

  • 1:RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

- (void)personInstanceMethod;

@end


#import "RevanPerson.h"

@implementation RevanPerson

@end

  • 2:測試代碼
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    RevanPerson *person = [[RevanPerson alloc] init];
    
    [person personInstanceMethod];
}

@end
崩潰輸出:
2018-07-12 14:46:51.714853+0800 01-Runtime_objc_msgSend[2616:75078] -[RevanPerson personInstanceMethod]: unrecognized selector sent to instance 0x6000000151d0
2018-07-12 14:46:51.727333+0800 01-Runtime_objc_msgSend[2616:75078] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[RevanPerson personInstanceMethod]: unrecognized selector sent to instance 0x6000000151d0'
  • 這是找不到方法最常見的崩潰信息。經(jīng)過上面圖中的分析來看,我們了解了在發(fā)送一個消息并且尋找這個消息的過程,在我們看來尋遍上圖的過程后如果依然沒有找到就會崩潰,輸出上面的錯誤信息。在尋找消息的過程中如果經(jīng)歷了上面的過程依然沒有找到就會查看有無方法動態(tài)解析,這就是下面討論的
  • 消息發(fā)送流程
    objc_msgSend消息發(fā)送.png

三、動態(tài)方法解析

  • 在動態(tài)方法解析中可以讓代碼執(zhí)行其他的方法,好比上面的問題,因為沒有實現(xiàn)personInstanceMethod方法,所以程序會因為找不到方法而崩潰。為了不讓程序崩潰,我們可以重寫resolveInstanceMethod:或者resolveClassMethod:方法來給personInstanceMethod方法添加實現(xiàn)

1、instance方法的動態(tài)方法解析

  • 1.1:RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

- (void)personInstanceMethod;

@end

#import "RevanPerson.h"
#import <objc/runtime.h>

@implementation RevanPerson

/**
 動態(tài)解析instanceMethod方法

 @param sel 發(fā)送的具體消息
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(personInstanceMethod)) {
        // 把一個方法構(gòu)造成一個 Method類型
        Method method = class_getInstanceMethod(self, @selector(resolveTest));
        // 獲取Method類型中方法的地址
        IMP imp = method_getImplementation(method);
        // 獲取Method類型參數(shù)
        const char *types = method_getTypeEncoding(method);
        // 給類添加對象方法,給元類添加類方法
        class_addMethod(self, sel, imp, types);
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

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

@end
  • 1.2:測試代碼
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    RevanPerson *person = [[RevanPerson alloc] init];
    
    [person personInstanceMethod];
}

@end
打印輸出:
2018-07-12 15:31:48.004090+0800 01-Runtime_objc_msgSend[4588:111337] -[RevanPerson resolveTest]
  • 會發(fā)現(xiàn)我們外面調(diào)用的person對象的personInstanceMethod方法,但是真正調(diào)用的是resolveTest方法,這就是動態(tài)方法解析的魅力所在,這也是動態(tài)語言的魅力所在
  • 這次也更好的讓我們認(rèn)識到,OC在調(diào)用方法的本質(zhì)是給instance對象、類對象發(fā)送消息,當(dāng)你在使用類對象調(diào)用類方法的時候,發(fā)現(xiàn)執(zhí)行的是instance方法時,不要奇怪。
  • class_getInstanceMethod函數(shù)是用來把一個方法構(gòu)造成一個 Method類型結(jié)構(gòu)體
  • method_getImplementation函數(shù)是用來獲取Method類型結(jié)構(gòu)體中方法的地址
  • method_getTypeEncoding函數(shù)是用來獲取Method類型結(jié)構(gòu)體中參數(shù)
  • class_addMethod函數(shù)用來給class對象和meta-class對象添加方法
/**
 添加方法
 
 @param cls 給類添加instance方法;給元類添加類方法
 @param name 消息名稱
 @param imp 方法地址
 @param types 方法參數(shù)和返回值信息
 */
 class_addMethod(cls, name, imp, types)

2、instance方法的動態(tài)方法解析調(diào)用函數(shù)

  • RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

- (void)personInstanceMethod;

@end

#import "RevanPerson.h"
#import <objc/runtime.h>

@implementation RevanPerson

void resolveFunc(id self, SEL _cmd) {
    NSLog(@"%@, %s", self, _cmd);
}

/**
 動態(tài)解析instanceMethod方法
 
 @param sel 發(fā)送的具體消息
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(personInstanceMethod)) {
        
        // 給類添加對象方法,給元類添加類方法
        class_addMethod(self, sel, resolveFunc, "V16@0:8");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end
  • 測試代碼
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    RevanPerson *person = [[RevanPerson alloc] init];
    
    [person personInstanceMethod];
}

@end
打印輸出:
2018-07-12 15:53:25.849709+0800 01-Runtime_objc_msgSend[4801:126769] <RevanPerson: 0x600000014310>, personInstanceMethod

3、class方法的動態(tài)方法解析

  • 1:RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

+ (void)personClassMethod;

@end

#import "RevanPerson.h"
#import <objc/runtime.h>

@implementation RevanPerson

/**
 動態(tài)解析instanceMethod方法
 
 @param sel 發(fā)送的具體消息
 */
+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(personClassMethod)) {
        // 把一個方法構(gòu)造成一個 Method類型數(shù)據(jù)結(jié)構(gòu)
        Method method = class_getClassMethod(self, @selector(resolveTest));
        // 獲取Method類型中方法的地址
        IMP imp = method_getImplementation(method);
        // 獲取Method類型參數(shù)
        const char *types = method_getTypeEncoding(method);
        // 給類添加對象方法,給元類添加類方法
        class_addMethod(object_getClass(self), sel, imp, types);
        return YES;
    }
    return [super resolveClassMethod:sel];
}

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

@end

  • 2、測試代碼
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [RevanPerson personClassMethod];
}

@end
打印輸出:
2018-07-12 16:08:05.664423+0800 01-Runtime_objc_msgSend[5018:140031] +[RevanPerson resolveTest]
  • 小結(jié)
    • 在構(gòu)造Method類型的結(jié)構(gòu)體時要使用class_getClassMethod函數(shù)
    • 在使用class_addMethod添加類方法時,第一個參數(shù)要傳入元類對象

4、Class方法的動態(tài)方法解析調(diào)用函數(shù)

  • 1:RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

+ (void)personClassMethod;

@end

#import "RevanPerson.h"
#import <objc/runtime.h>

@implementation RevanPerson

void resolveFunc(id self, SEL _cmd) {
    NSLog(@"%@, %s", self, _cmd);
}

/**
 動態(tài)解析instanceMethod方法

 @param sel 發(fā)送的具體消息
 */
+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(personClassMethod)) {

        // 給類添加對象方法,給元類添加類方法
        class_addMethod(object_getClass(self), sel, resolveFunc, "V16@0:8");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

@end
  • 2:測試代碼
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [RevanPerson personClassMethod];
}

@end
打印輸出:
2018-07-12 16:13:53.628104+0800 01-Runtime_objc_msgSend[5077:144122] RevanPerson, personClassMethod
  • 動態(tài)方法解析流程
    動態(tài)方法解析.png

四、消息轉(zhuǎn)發(fā)

  • 當(dāng)在動態(tài)方法解析過程中程序員沒有重寫resolveClassMethod方法和resolveInstanceMethod方法,就會進入消息轉(zhuǎn)發(fā)階段
  • 消息轉(zhuǎn)發(fā)顧名思義,因為自己沒有辦法處理這個方法,所以要讓其他對象來處理

1、轉(zhuǎn)給其他對象處理

  • 1:RevanTest
#import <Foundation/Foundation.h>

@interface RevanTest : NSObject

- (void)personInstanceMethod;
@end

#import "RevanTest.h"

@implementation RevanTest

- (void)personInstanceMethod {
    NSLog(@"%s", __func__);
}
@end
  • 2:RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

- (void)personInstanceMethod;

@end

#import "RevanPerson.h"
#import <objc/runtime.h>
#import "RevanTest.h"

@implementation RevanPerson

- (id)forwardingTargetForSelector:(SEL)aSelector {
    
    if (aSelector == @selector(personInstanceMethod)) {
        return [[RevanTest alloc] init];
    }
    
    return [super forwardingTargetForSelector:aSelector];
}

@end
  • 3:測試代碼
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    RevanPerson *person = [[RevanPerson alloc] init];
    [person personInstanceMethod];
}

@end
打印輸出:
2018-07-12 16:33:34.707800+0800 01-Runtime_objc_msgSend[5414:161031] -[RevanTest personInstanceMethod]
  • 從輸出可以看出是輸出RevanTest對象中同名的personInstanceMethod方法
  • 處理的對象中的方法必須要和當(dāng)前發(fā)送的消息名稱、參數(shù)類型保持一致,否則會崩潰
  • 4、只要是方法名SEL相同,至于是調(diào)用了RevanTest對象中的personInstanceMethod對象方法還是personInstanceMethod類方法都有可以能
  • 4.1:RevanTest
#import <Foundation/Foundation.h>

@interface RevanTest : NSObject

+ (void)personInstanceMethod;
- (void)personInstanceMethod;

@end

#import "RevanTest.h"

@implementation RevanTest

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

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

@end
  • 4.2:RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

- (void)personInstanceMethod;
@end

#import "RevanPerson.h"
#import "RevanTest.h"

@implementation RevanPerson

- (id)forwardingTargetForSelector:(SEL)aSelector {
    
    if (aSelector == @selector(personInstanceMethod)) {
        //返回的是class對象
        return [RevanTest class];
    }
    
    return [super forwardingTargetForSelector:aSelector];
}
@end
  • 4.3:測試代碼
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    RevanPerson *person = [[RevanPerson alloc] init];
    //調(diào)用的是實例方法
    [person personInstanceMethod];
    
}
@end
打印輸出:
2018-07-12 22:59:31.363051+0800 02-runtime[1452:65484] +[RevanTest personInstanceMethod]
  • 調(diào)用的是類方法中的實現(xiàn)
  • forwardingTargetForSelector:方法的返回值為nil或者返回值中不包含aSelector方法時都會崩潰
  • 當(dāng)forwardingTargetForSelector:沒有返回處理轉(zhuǎn)發(fā)消息的對象時,系統(tǒng)調(diào)用方法簽名方法中,可以在方法簽名的執(zhí)行代碼中做任何事

2、方法簽名

  • 1:RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

- (void)personInstanceMethod;
@end

#import "RevanPerson.h"
#import "RevanTest.h"

@implementation RevanPerson

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(personInstanceMethod)) {
        //返回方法簽名:也就是aSelector的參數(shù)
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

// 滿足方法簽名后就會調(diào)用forwardInvocation:方法
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"%s", __func__);
}

@end
  • 2:測試代碼
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    RevanPerson *person = [[RevanPerson alloc] init];
    //調(diào)用的是實例方法
    [person personInstanceMethod];
    
}
@end
打印輸出:
2018-07-12 23:24:51.887746+0800 02-runtime[1817:84543] -[RevanPerson forwardInvocation:]
  • 所以可以在forwardInvocation:中做任何事
  • 類方法也是同樣的道理,只需要把方法前面的"-"改為"+"就可以了
  • 消息轉(zhuǎn)發(fā)流程
    消息轉(zhuǎn)發(fā)流程.png

五、源碼

/***********************************************************************
* lookUpImpOrForward.
* The standard IMP lookup. 
* initialize==NO tries to avoid +initialize (but sometimes fails)
* cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)
* Most callers should use initialize==YES and cache==YES.
* inst is an instance of cls or a subclass thereof, or nil if none is known. 
*   If cls is an un-initialized metaclass then a non-nil inst is faster.
* May return _objc_msgForward_impcache. IMPs destined for external use 
*   must be converted to _objc_msgForward or _objc_msgForward_stret.
*   If you don't want forwarding at all, use lookUpImpOrNil() instead.
**********************************************************************/
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup
    if (cache) {//是否有緩存,來查找緩存
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    // runtimeLock is held during isRealized and isInitialized checking
    // to prevent races against concurrent realization.

    // runtimeLock is held during method search to make
    // method-lookup + cache-fill atomic with respect to method addition.
    // Otherwise, a category could be added but ignored indefinitely because
    // the cache was re-filled with the old value after the cache flush on
    // behalf of the category.

    runtimeLock.read();

    if (!cls->isRealized()) {
        // Drop the read-lock and acquire the write-lock.
        // realizeClass() checks isRealized() again to prevent
        // a race while the lock is down.
        runtimeLock.unlockRead();
        runtimeLock.write();

        realizeClass(cls);

        runtimeLock.unlockWrite();
        runtimeLock.read();
    }
    //沒有初始化
    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlockRead();
        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.read();
        // If sel == initialize, _class_initialize will send +initialize and 
        // then the messenger will send +initialize again after this 
        // procedure finishes. Of course, if this is not being called 
        // from the messenger then it won't happen. 2778172
    }

    
 retry:    
    runtimeLock.assertReading();
#pragma mark - 一:消息發(fā)送
    // Try this class's cache.
//MARK:1.1:從類的緩存中尋找
    imp = cache_getImp(cls, sel);
    if (imp) goto done;


//MARK:1.2:如果緩存中沒有,就從類對象的方法列表中尋找(class_rw_t中的 methods中尋找)
    // Try this class's method lists.
    {
        //獲取meth
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            //如果找在類對象的方法列表中找到方法,將找到的方法緩存到當(dāng)前cls中
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }
//MARK:1.3:從父類的緩存和方法列表中尋找
    // Try superclass caches and method lists.
    {
        unsigned attempts = unreasonableClassCount();
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            // Halt if there is a cycle in the superclass chain.
            if (--attempts == 0) {
                _objc_fatal("Memory corruption in class list.");
            }
//MARK:1.3.1、從父類的緩存中尋找
            // Superclass cache.
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    // Found the method in a superclass. Cache it in this class.
                    // 在某一個父類的緩存中找到了方法,并且緩存到cls類的緩存中
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    // Found a forward:: entry in a superclass.
                    // Stop searching, but don't cache yet; call method 
                    // resolver for this class first.
                    break;
                }
            }
//MARK:1.3.2、從父類的方法列表中尋找
            // Superclass method list.
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                // 在某一個父類的方法列表中找到了方法,并且緩存到cls類的緩存中
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }
#pragma mark - 二:動態(tài)方法解析
    // No implementation found. Try method resolver once.
    // triedResolver初始值為 NO
    if (resolver  &&  !triedResolver) {
        runtimeLock.unlockRead();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.read();
        // Don't cache the result; we don't hold the lock so it may have 
        // changed already. Re-do the search from scratch instead.
        triedResolver = YES;
        goto retry;
    }
    
#pragma mark - 三:消息轉(zhuǎn)發(fā)
    // No implementation found, and method resolver didn't help. 
    // Use forwarding.

    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlockRead();

    return imp;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,643評論 2 380

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