OC-Runtime-常用API

OC-Runtime-常用API

image-20210510151310311
image-20210510151322735
image-20210510151337970
image-20210510151352812
image-20210510151410706

一. 類相關(guān)API

//動(dòng)態(tài)創(chuàng)建一個(gè)類(參數(shù):父類蒋失,類名,額外的內(nèi)存空間)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)

//注冊(cè)一個(gè)類(要在類注冊(cè)之前添加成員變量)
void objc_registerClassPair(Class cls)

//銷毀一個(gè)類
void objc_disposeClassPair(Class cls)

//獲取對(duì)象的isa指向的Class
Class object_getClass(id obj)

//設(shè)置對(duì)象的isa指向的Class
Class object_setClass(id obj, Class cls)

//判斷一個(gè)對(duì)象是否為Class
BOOL object_isClass(id obj)

//判斷一個(gè)Class是否為元類
BOOL class_isMetaClass(Class cls)

//獲取父類
Class class_getSuperclass(Class cls)
  • Class object_getClass(id _Nullable obj) 獲取 isa指向的Class
***********************??MJPerson.h ??**************************
#import <Foundation/Foundation.h>

@interface MJPerson : NSObject
@property (assign, nonatomic) int ID;
@property (assign, nonatomic) int weight;
@property (assign, nonatomic) int age;
@property (copy, nonatomic) NSString *name;
- (void)run;
@end

***********************??MJPerson.m ??**************************  
#import "MJPerson.h"

@implementation MJPerson

- (void)print
- (void)run
{
    NSLog(@"%s", __func__);
}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        MJPerson *person = [[MJPerson alloc] init];
        class_isMetaClass(object_getClass([MJPerson class]));
        
        Class cls = object_getClass(person);//實(shí)例對(duì)象的isa指向類對(duì)象
        Class metaClass = object_getClass([MJPerson class]);//類對(duì)象的isa指向元類對(duì)象
        
        NSLog(@"是元類對(duì)象 %d", class_isMetaClass(metaClass));
        NSLog(@"%d %d %d",
              object_isClass(person),
              object_isClass([MJPerson class]),
              object_isClass(object_getClass([MJPerson class]))
              );
        
    }
    return 0;
}

RUN????????????

2021-05-10 15:26:50.390907+0800 Interview02-runtime應(yīng)用[3278:157126] 是元類對(duì)象 1
2021-05-10 15:26:50.391387+0800 Interview02-runtime應(yīng)用[3278:157126] 0 1 1

Class object_setClass(id _Nullable obj, Class _Nonnull cls) 設(shè)置 isa的指向的Class

***********************?? MJCar.h ??**************************
@interface MJCar : NSObject
- (void)run;
@end

***********************?? MJCar.m ??**************************
@implementation MJCar
- (void)run
{
    NSLog(@"%s", __func__);
}
@end
***********************?? main.m ??**************************
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        MJPerson *person = [[MJPerson alloc] init];
        [person run];
        
        object_setClass(person, [MJCar class]);
        [person run];
        
        NSLog(@"%d %d %d",
              object_isClass(person),
              object_isClass([MJPerson class]),
              object_isClass(object_getClass([MJPerson class]))
              );
        
    }
    return 0;
}

RUN????????????

2021-05-10 15:29:57.963729+0800 Interview02-runtime應(yīng)用[3304:159036] -[MJPerson run]
2021-05-10 15:29:57.964308+0800 Interview02-runtime應(yīng)用[3304:159036] -[MJCar run]
2021-05-10 15:29:57.964363+0800 Interview02-runtime應(yīng)用[3304:159036] 0 1 1
  • objc_allocateClassPair

Class objc_allocateClassPair(Class superclass, const char * name, size_t extraBytes)
動(dòng)態(tài)創(chuàng)建一個(gè)類,(參數(shù):父類,類名,額外的存儲(chǔ)空間)

objc_registerClassPair(Class cls)注冊(cè)一個(gè)類 (要在類注冊(cè)之前添加成員變量)

void run(id self, SEL _cmd)
{
    NSLog(@"_____ %@ - %@", self, NSStringFromSelector(_cmd));
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        // 創(chuàng)建類柴底,傳入父類和類名
            Class newClass = objc_allocateClassPair([NSObject class], "MJDog", 0);
            // 注冊(cè)類之前添加成員變量
            class_addIvar(newClass, "_age", 4, 1, @encode(int));
            class_addIvar(newClass, "_weight", 4, 1, @encode(int));
            class_addMethod(newClass, @selector(run), (IMP)run, "v@:");
            // 注冊(cè)類
            objc_registerClassPair(newClass);

            id dog = [[newClass alloc] init];
            [dog setValue:@10 forKey:@"_age"];
            [dog setValue:@20 forKey:@"_weight"];
            [dog run];

            NSLog(@"%@ %@", [dog valueForKey:@"_age"], [dog valueForKey:@"_weight"]);

            MJPerson *person = [[MJPerson alloc] init];
            //修改person對(duì)象isa指向
            object_setClass(person, newClass);
            [person run];

            // 在不需要這個(gè)類時(shí)釋放
//            objc_disposeClassPair(newClass);
        
    }
    return 0;
}

RUN>????????????

2021-05-10 15:36:09.336005+0800 Interview02-runtime應(yīng)用[3430:165339] _____ <MJDog: 0x10067f380> - run
2021-05-10 15:36:09.336861+0800 Interview02-runtime應(yīng)用[3430:165339] 10 20
2021-05-10 15:36:09.337011+0800 Interview02-runtime應(yīng)用[3430:165339] _____ <MJDog: 0x100686fd0> - run

在程序運(yùn)行的時(shí)候,動(dòng)態(tài)添加一個(gè)類秸歧,并且添加成員變量泌射、方法,最后使用類宰翅。

  1. 一定要在注冊(cè)類之前添加成員變量噩死,因?yàn)槌蓡T變量是在_r_o_t表里面颤难,是只讀的,所以要在類的結(jié)構(gòu)確定之前添加成員變量已维。
  2. 不能使用class_addIvar給已經(jīng)創(chuàng)建的類添加成員變量行嗤,因?yàn)橐呀?jīng)創(chuàng)建的類的結(jié)構(gòu)在代碼寫完就已經(jīng)確定好了,程序運(yùn)行中就不能給已經(jīng)創(chuàng)建的類添加成員變量了垛耳。
  3. 方法可以在注冊(cè)類之后添加栅屏,因?yàn)榉椒ㄊ窃赺r_w_t表里面飘千,是可讀可寫的。

二. 成員變量相關(guān)API

//獲取類中指定名稱實(shí)例成員變量的信息
Ivar class_getInstanceVariable(Class cls, const char *name)

//獲取成員變量的相關(guān)信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)

//設(shè)置和獲取成員變量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)

//拷貝實(shí)例變量列表(最后需要調(diào)用free釋放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

//動(dòng)態(tài)添加成員變量(已經(jīng)注冊(cè)的類是不能動(dòng)態(tài)添加成員變量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
  • Ivar class_getInstanceVariable 獲取一個(gè)實(shí)例變量的信息
  • object_setIvar(id obj, Ivar ivar, id value)設(shè)置實(shí)例變量的值
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //獲取類中指定名稱實(shí)例成員變量的信息
        //傳入的是一個(gè)類對(duì)象栈雳,所以只能獲取成員變量的信息护奈,并不能獲取成員變量的值
        Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age");
        Ivar nameIvar = class_getInstanceVariable([MJPerson class], "_name");

        NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));
        //打印:_age i   i代表字符編碼int

        MJPerson *person = [[MJPerson alloc] init];
        //設(shè)置成員變量的值
        //傳入的是一個(gè)實(shí)例對(duì)象哥纫,所以可以設(shè)置成員變量的值
        object_setIvar(person, nameIvar, @"123");
        object_setIvar(person, ageIvar, (__bridge id)(void *)10);
        //獲取成員變量的值
        id name = object_getIvar(person, nameIvar);

        NSLog(@"%@ %d", name, person.age);
        //打用蛊臁:123 10
        
    }
    return 0;
}

RUN> ??????????????????

2021-05-10 15:40:12.967391+0800 Interview02-runtime應(yīng)用[3454:168018] _age i
2021-05-10 15:40:12.967815+0800 Interview02-runtime應(yīng)用[3454:168018] 123 10

object_setIvar(person, ageIvar, (__bridge id)(void *)10);上面runtimeAPI內(nèi)部沒(méi)做轉(zhuǎn)換,所以需要傳什么值就傳什么值蛀骇,但是要做一些數(shù)據(jù)類型轉(zhuǎn)換(先轉(zhuǎn)成指針類型厌秒,再轉(zhuǎn)成id類型)。

如果是KVC的value值擅憔,可以傳NSNumber類型的值鸵闪,因?yàn)镵VC內(nèi)部會(huì)做轉(zhuǎn)換:[@10 integerValue]。

  • BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)動(dòng)態(tài)添加成員變量 (已經(jīng)注冊(cè)的類是不能添加成員變量的)
  • const char *ivar_getName(Ivar v) 獲取成員變量 name
  • const char * ivar_getTypeEncoding(Ivar v) 獲取成員變量字符串編碼
  • Ivar * class_copyIvarList(Class cls, unsigned int * outCount)拷貝實(shí)例變量列表,最后需要調(diào)用free釋放.

用途一: 獲取系統(tǒng)類私有的成員變量 (這種方式在 iOS13 后 已經(jīng)被禁用了,iOS13 后系統(tǒng)禁止訪問(wèn)一些私有的成員變量)

    unsigned int count;
    Ivar *ivar = class_copyIvarList([UITextField class], &count);
    for (int i = 0; i < count; i ++) {
        Ivar iva = ivar[I];
        NSLog(@"%s",ivar_getName(iva));
    }
    self.nameTF.placeholder = @"請(qǐng)輸入姓名";
    [self.nameTF setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
    free(ivar);

用途二:字典轉(zhuǎn)模型

+ (instancetype)json2Model:(NSDictionary *)json{
    id obj = [[self alloc]init];
    unsigned int count;
    Ivar *ivars = class_copyIvarList([self class], &count);
    //遍歷所有的成員變量
    for (int i = 0; i < count; i ++) {
        Ivar iva = ivars[I];
        NSMutableString *ivarStr = [NSMutableString stringWithUTF8String:ivar_getName(iva)];
        //去掉成員變量前面的 _
        [ivarStr deleteCharactersInRange:NSMakeRange(0, 1)];
        
        [obj setValue:json[ivarStr] forKey:ivarStr];
    }
    
    return obj;
}

這樣寫會(huì)有很多問(wèn)題,這只是個(gè)思路,僅供參考

用途三:歸檔,解檔

- (instancetype)initWithCoder:(NSCoder *)coder{
    if (self = [super init]) {
        unsigned int count;
        Ivar *ivars = class_copyIvarList([self class], &count);
        //遍歷所有的成員變量
        for (int i = 0; i < count; i ++) {
            Ivar iva = ivars[I];
            NSMutableString *ivarStr = [NSMutableString stringWithUTF8String:ivar_getName(iva)];
            //去掉成員變量前面的 _
            [ivarStr deleteCharactersInRange:NSMakeRange(0, 1)];
            //從文件中取出值
            id value = [coder decodeObjectForKey:ivarStr];
            //賦值到對(duì)象中
            [self setValue:value forKey:ivarStr];
        }
    }
    return self;
}


- (void)encodeWithCoder:(NSCoder *)coder{
    
    unsigned int count;
    Ivar *ivars = class_copyIvarList([self class], &count);
    //遍歷所有的成員變量
    for (int i = 0; i < count; i ++) {
        Ivar iva = ivars[I];
        NSMutableString *ivarStr = [NSMutableString stringWithUTF8String:ivar_getName(iva)];
        //去掉成員變量前面的 _
        [ivarStr deleteCharactersInRange:NSMakeRange(0, 1)];
        //從對(duì)象中取出對(duì)應(yīng)的值
        id value = [self valueForKey:ivarStr];
        //歸檔到文件中
        [coder encodeObject:value forKey:ivarStr];
    }
}

三. 屬性相關(guān)API

//獲取一個(gè)屬性
objc_property_t class_getProperty(Class cls, const char *name)

//拷貝屬性列表(最后需要調(diào)用free釋放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)

//動(dòng)態(tài)添加屬性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                       unsigned int attributeCount)

//動(dòng)態(tài)替換屬性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                           unsigned int attributeCount)

//獲取屬性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)

四. 方法相關(guān)API

//獲取一個(gè)實(shí)例方法暑诸、類方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)

//根據(jù)class和方法名獲取方法的imp
IMP class_getMethodImplementation(Class cls, SEL name)
//設(shè)置方法的imp
IMP method_setImplementation(Method m, IMP imp)
//交換方法的imp
void method_exchangeImplementations(Method m1, Method m2)
//獲取方法名
SEL method_getName(Method m)
//獲取imp
IMP method_getImplementation(Method m)
//獲取方法返回值類型岛马、參數(shù)類型的編碼
const char *method_getTypeEncoding(Method m)
//獲取參數(shù)個(gè)數(shù)
unsigned int method_getNumberOfArguments(Method m)
//獲取返回值類型
char *method_copyReturnType(Method m)
//根據(jù)index獲取參數(shù)
char *method_copyArgumentType(Method m, unsigned int index)

//拷貝方法列表(最后需要調(diào)用free釋放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)

//動(dòng)態(tài)添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

//動(dòng)態(tài)替換方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

//根據(jù)SEL獲取名字
const char *sel_getName(SEL sel)
//根據(jù)字符串包裝成一個(gè)SEL,和@selector("方法名字")方法等效
SEL sel_registerName(const char *str)

//根據(jù)block返回一個(gè)imp
IMP imp_implementationWithBlock(id block)
//根據(jù)imp返回一個(gè)block
id imp_getBlock(IMP anImp)
//移除imp對(duì)應(yīng)的block
BOOL imp_removeBlock(IMP anImp)
void myrun()
{
    NSLog(@"---myrun");
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJPerson *person = [[MJPerson alloc] init];
        
        class_replaceMethod([MJPerson class], @selector(run), (IMP)myrun, "v");
        [person run];
    }
    return 0;
}

RUN> ??????????????

2021-05-10 15:47:37.466763+0800 Interview04-方法[3531:172517] ---myrun
  • 將block當(dāng)做方法實(shí)現(xiàn)
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJPerson *person = [[MJPerson alloc] init];
        
        class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{
            NSLog(@"123123");
        }), "v");
        [person run];
    }
    return 0;
}

RUN>????????????

2021-05-10 15:49:19.473743+0800 Interview04-方法[3570:174628] 123123
  • 交換方法實(shí)現(xiàn)
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJPerson *person = [[MJPerson alloc] init];

        Method runMethod = class_getInstanceMethod([MJPerson class], @selector(run));
        Method testMethod = class_getInstanceMethod([MJPerson class], @selector(test));
        method_exchangeImplementations(runMethod, testMethod);

        [person run];
    }
    return 0;
}

RUN> ????????????

2021-05-10 15:51:18.409680+0800 Interview04-方法[3593:175998] -[MJPerson test]

交換方法實(shí)現(xiàn)的使用

交換方法實(shí)現(xiàn)在開(kāi)發(fā)中經(jīng)常使用屠列,但是實(shí)際上我們使用最多的是交換系統(tǒng)或者第三方框架的方法。

攔截所有按鈕的點(diǎn)擊事件:

UIButton繼承于UIControl伞矩,UIControl有一個(gè)sendAction:to:forEvent:方法笛洛,每當(dāng)觸發(fā)一個(gè)事件就會(huì)調(diào)用這個(gè)方法,所以我們可以給UIControl添加分類乃坤,在分類中交換這個(gè)方法的實(shí)現(xiàn):

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // hook:鉤子函數(shù)
        Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
        Method method2 = class_getInstanceMethod(self, @selector(mj_sendAction:to:forEvent:));
        method_exchangeImplementations(method1, method2);
    });
}

- (void)mj_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
    NSLog(@"%@-%@-%@", self, target, NSStringFromSelector(action));

    // 調(diào)用系統(tǒng)原來(lái)的實(shí)現(xiàn)
    // 因?yàn)榉椒ㄒ呀?jīng)交換了苛让,所以其實(shí)是調(diào)用sendAction:to:forEvent:
    [self mj_sendAction:action to:target forEvent:event];

    //攔截按鈕事件
    if ([self isKindOfClass:[UIButton class]]) {
        // 攔截了所有按鈕的事件

    }
}

上面交換方法也叫鉤子函數(shù),利用鉤子函數(shù)就實(shí)現(xiàn)了攔截所有UIButton的點(diǎn)擊事件湿诊。

問(wèn)題1:為什么上面要加個(gè)dispatch_once狱杰?

按理說(shuō)load方法只會(huì)調(diào)用一次,萬(wàn)一別人主動(dòng)調(diào)用了load方法那不就調(diào)用兩次了嗎厅须,這樣方法就交換兩次了和沒(méi)交換一樣仿畸,所以加個(gè)dispatch_once。

問(wèn)題2:交換方法實(shí)現(xiàn)的原理是什么朗和?

method_exchangeImplementations方法是傳入兩個(gè)Method错沽,以前我們講過(guò)Method的內(nèi)部結(jié)構(gòu),其實(shí)交換方法實(shí)現(xiàn)就是把Method里面的IMP交換了眶拉,如下圖:

交換前.png
交換后.png

上面說(shuō)的交換方法實(shí)現(xiàn)千埃,交換的是方法列表(methods數(shù)組)里面的method_t(也就是Method),如果這個(gè)方法有緩存忆植,怎么辦放可?

問(wèn)題3:如果這個(gè)方法有緩存谒臼,怎么辦?

其實(shí)耀里,調(diào)用method_exchangeImplementations函數(shù)會(huì)清空緩存蜈缤,這樣就保證了交換方法之后調(diào)用方法不會(huì)出錯(cuò)。

可以在objc4里面搜索到源碼:

void method_exchangeImplementations(Method m1, Method m2)
{
    if (!m1  ||  !m2) return;

    rwlock_writer_t lock(runtimeLock);

    IMP m1_imp = m1->imp;
    m1->imp = m2->imp;
    m2->imp = m1_imp;


    // RR/AWZ updates are slow because class is unknown
    // Cache updates are slow because class is unknown
    // fixme build list of classes whose Methods are known externally?

    flushCaches(nil);//交換IMP之后就會(huì)清空緩存备韧。

    updateCustomRR_AWZ(nil, m1);
    updateCustomRR_AWZ(nil, m2);
}

上面源碼很簡(jiǎn)單劫樟,可以發(fā)現(xiàn),交換IMP之后就會(huì)清空緩存织堂。

特別備注

本系列文章總結(jié)自MJ老師在騰訊課堂iOS底層原理班(下)/OC對(duì)象/關(guān)聯(lián)對(duì)象/多線程/內(nèi)存管理/性能優(yōu)化叠艳,相關(guān)圖片素材均取自課程中的課件。如有侵權(quán)易阳,請(qǐng)聯(lián)系我刪除附较,謝謝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末潦俺,一起剝皮案震驚了整個(gè)濱河市拒课,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌事示,老刑警劉巖早像,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異肖爵,居然都是意外死亡卢鹦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門劝堪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)冀自,“玉大人,你說(shuō)我怎么就攤上這事秒啦“敬郑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵余境,是天一觀的道長(zhǎng)驻呐。 經(jīng)常有香客問(wèn)我,道長(zhǎng)芳来,這世上最難降的妖魔是什么暴氏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮绣张,結(jié)果婚禮上答渔,老公的妹妹穿的比我還像新娘。我一直安慰自己侥涵,他們只是感情好沼撕,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布宋雏。 她就那樣靜靜地躺著,像睡著了一般务豺。 火紅的嫁衣襯著肌膚如雪磨总。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天笼沥,我揣著相機(jī)與錄音蚪燕,去河邊找鬼。 笑死奔浅,一個(gè)胖子當(dāng)著我的面吹牛馆纳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汹桦,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鲁驶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了舞骆?” 一聲冷哼從身側(cè)響起钥弯,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎督禽,沒(méi)想到半個(gè)月后脆霎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狈惫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年睛蛛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虱岂。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖菠红,靈堂內(nèi)的尸體忽然破棺而出第岖,到底是詐尸還是另有隱情,我是刑警寧澤试溯,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布蔑滓,位于F島的核電站,受9級(jí)特大地震影響遇绞,放射性物質(zhì)發(fā)生泄漏键袱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一摹闽、第九天 我趴在偏房一處隱蔽的房頂上張望蹄咖。 院中可真熱鬧,春花似錦付鹿、人聲如沸澜汤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)俊抵。三九已至谁不,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間徽诲,已是汗流浹背刹帕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谎替,地道東北人偷溺。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像院喜,于是被迫代替她去往敵國(guó)和親亡蓉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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