【技術(shù)整理】Swizzle應(yīng)用性研究

Swizzle的常見(jiàn)錯(cuò)誤及基本原理

示例1


@implementation UIImageView(TestContentMode_Origin)

+ (void)load {

    Method originMethod = class_getInstanceMethod([UIImageView class], @selector(setContentMode:));

    Method swizzledMethod = class_getInstanceMethod([UIImageView class], @selector(nty_setContentMode:));

    method_exchangeImplementations(originMethod, swizzledMethod);

}

- (void)nty_setContentMode:(UIViewContentMode)contentMode {

    NSLog(@"swizzle contentmode %@", self);

    [self nty_setContentMode:contentMode];

}

@end

效果:程序崩潰

崩潰原因分析

method_exchangeImplementations是將兩個(gè)SEL指向的IMP互相替換。

originMethod想指向UIImageView的方法setContentMode乌叶,然而該方法是UIImageView的父類UIView實(shí)現(xiàn)的急鳄,所以UIImageView分類中的方法實(shí)際上是與UIView的setContentMode做了替換徐钠。在UIView的實(shí)例調(diào)用setContentMode時(shí)偷仿,會(huì)調(diào)用nty_setContentMode的SEL烁焙,UIView中沒(méi)有實(shí)現(xiàn)此方法抵蚊,導(dǎo)致崩潰.

見(jiàn)圖1,2

圖1
圖2

引申:Method, SEL, IMP


// Method 在頭文件 objc_class.h中定義如下:

typedef struct objc_method *Method;

typedef struct objc_method {

    SEL method_name;

    char *method_types;

    IMP method_imp;

};

// SEL的定義為:

typedef struct objc_selector  *SEL; 

// IMP 的含義:

typedef id (*IMP)(id, SEL, ...);

SEL的定義為:是一個(gè)指向 objc_selector 指針施绎,表示方法的名字/簽名。

IMP 的含義:是一個(gè)函數(shù)指針贞绳,這個(gè)被指向的函數(shù)包含一個(gè)接收消息的對(duì)象id(self 指針), 調(diào)用方法的選標(biāo) SEL (方法名)谷醉,以及不定個(gè)數(shù)的方法參數(shù),并返回一個(gè)id冈闭。也就是說(shuō) IMP 是消息最終調(diào)用的執(zhí)行代碼俱尼,是方法真正的實(shí)現(xiàn)代碼 。

引申:class


struct objc_class {

    struct objc_class super_class;  /*父類*/

    const char *name;                /*類名字*/

    long version;                  /*版本信息*/

    long info;                        /*類信息*/

    long instance_size;              /*實(shí)例大小*/

    struct objc_ivar_list *ivars;    /*實(shí)例參數(shù)鏈表*/

    struct objc_method_list **methodLists;  /*方法鏈表*/

    struct objc_cache *cache;              /*方法緩存*/

    struct objc_protocol_list *protocols;  /*協(xié)議鏈表*/

};

methodLists方法鏈表里面存儲(chǔ)的是Method 類型萎攒。selector 就是指 Method的 SEL, address就是指Method的 IMP遇八。

示例1優(yōu)化

示例1證明,直接使用method_exchangeImplementations進(jìn)行swizzle耍休,有可能出現(xiàn)崩潰問(wèn)題刃永。使用第三方庫(kù)JRSwizzle的方法jr_swizzleMethod:withMethod:error:對(duì)該問(wèn)題進(jìn)行了優(yōu)化。


+ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ {

#if OBJC_API_VERSION >= 2

Method origMethod = class_getInstanceMethod(self, origSel_);

if (!origMethod) {

...(容錯(cuò)處理羊精,節(jié)約篇幅斯够,省略)

return NO;

}

Method altMethod = class_getInstanceMethod(self, altSel_);

if (!altMethod) {

...(容錯(cuò)處理,節(jié)約篇幅喧锦,省略)

return NO;

}

class_addMethod(self,

origSel_,

class_getMethodImplementation(self, origSel_),

method_getTypeEncoding(origMethod));

class_addMethod(self,

altSel_,

class_getMethodImplementation(self, altSel_),

method_getTypeEncoding(altMethod));

method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_));

return YES;

#else

...(低版本API的配置方式读规,節(jié)約篇幅,省略)

#endif

}

該方法通過(guò)class_addMethod保證在父類實(shí)現(xiàn)原生方法或被swizzle方法而子類沒(méi)有實(shí)現(xiàn)的情況下燃少,重新生成一個(gè)新的Method束亏,SEL不變,IMP指向父類方法的IMP阵具,保存在子類的method_list中(即將子類中實(shí)現(xiàn)同樣的方法)碍遍。

class_addMethod:如果發(fā)現(xiàn)方法已經(jīng)存在扶欣,會(huì)失敗返回艰毒,也可以用來(lái)做檢查用,我們這里是為了避免源方法沒(méi)有實(shí)現(xiàn)的情況;如果方法沒(méi)有存在,我們則先嘗試添加被替換的方法的實(shí)現(xiàn)

示例2

通過(guò)jr_swizzleMethod:withMethod:error:進(jìn)行setContentMode的swizzle


@implementation UIImageView (TestContentMode_JR)

+ (void)load {   

    [[UIImageView class] jr_swizzleMethod:@selector(setContentMode:) withMethod:@selector(nty_setContentMode:) error:nil];

}

- (void)nty_setContentMode:(UIViewContentMode)contentMode {

    NSLog(@"swizzle contentmode(JR) %@", self);

    [self nty_setContentMode:contentMode];

}

@end

該方法中晋修,在class_addMethod時(shí)隅要,見(jiàn)圖3.

圖3

在method_exchangeImplementations后惜论,見(jiàn)圖4.

圖4

當(dāng)前凯旭,可以完美解決方問(wèn)題

示例3

針對(duì)示例1, 如果不使用jr_swizzleMethod:withMethod:error:的方式惫霸,仍有辦法解決此問(wèn)題封恰。

示例1之所以崩潰是因?yàn)樵赨IView執(zhí)行setContentMode時(shí)矮烹,會(huì)調(diào)用UIView不存在的方法nty_setContentMode越庇。那么,將swizzle的方法從UIImageView的分類中改為寫(xiě)在UIView的分類中奉狈,即可解決此問(wèn)題卤唉。


@implementation UIView(TestContentMode_Origin)

+ (void)load {

    Method originMethod = class_getInstanceMethod([UIView class], @selector(setContentMode:));

    Method swizzledMethod = class_getInstanceMethod([UIView class], @selector(nty_setContentMode:));

    method_exchangeImplementations(originMethod, swizzledMethod);

}

- (void)nty_setContentMode:(UIViewContentMode)contentMode {

    if ([self isKindOfClass:[UIImageView class]]) {

      NSLog(@"swizzle contentmode %@", self);

    }

    [self nty_setContentMode:contentMode];

}

@end

示例4

若由于需求原因,既有針對(duì)UIView的setContentMode的swizzle方法仁期,也有針對(duì)UIImageView的swizzle方法(即示例2與示例3共存)桑驱。將會(huì)發(fā)生邏輯錯(cuò)誤。

兩個(gè)swizzle都是寫(xiě)在分類的+load方法中跛蛋,兩方法的調(diào)用順序與build phase中的文件編繹順序有關(guān)熬的。此處,我們假設(shè)UIView (TestContentMode_Origin)的+load先被調(diào)用

見(jiàn)圖5

圖5

UIImageView(TestContentMode_Origin)的+load再被調(diào)用

見(jiàn)圖6?7

圖6
圖7

那么此時(shí)赊级,若UIView調(diào)用setContentMode不會(huì)有問(wèn)題押框,UIImageView調(diào)用時(shí)會(huì)出現(xiàn)無(wú)限調(diào)用循環(huán)的問(wèn)題

拓展:RSSwizzle提供了另外一種更加健壯的Swizzle方式,如以下代碼所示理逊。但此代碼在我們項(xiàng)目中沒(méi)有普及橡伞,我也沒(méi)有確認(rèn)此方法是否會(huì)出現(xiàn)其他問(wèn)題,此處列出僅供參考晋被。


RSSwizzleInstanceMethod([UIView class],

                            @selector(setContentMode:),

                            RSSWReturnType(void),

                            RSSWArguments(UIViewContentMode contentMode),

                            RSSWReplacement({

    // Returning modified return value.

    NSLog(@"swizzle contentmode %@", @(contentMode));

    // 先執(zhí)行原始方法

    RSSWCallOriginal();

                            }), 0, NULL);

示例5

針對(duì)示例4的需求兑徘,建議將UIImageView的swizzle方法寫(xiě)到UIView的分類中。即示例3的代碼羡洛。那么代碼會(huì)變成以下的樣式挂脑。


@implementation UIView(ForUIViewSwizzle)

+ (void)load {

    Method originMethod = class_getInstanceMethod([UIView class], @selector(setContentMode:));

    Method swizzledMethod = class_getInstanceMethod([UIView class], @selector(nty_setContentMode:));

    method_exchangeImplementations(originMethod, swizzledMethod);

}

- (void)nty_setContentMode:(UIViewContentMode)contentMode {

    // 執(zhí)行針對(duì)UIImageView的swizzle的邏輯

    [self nty_setContentMode:contentMode];

}

@end

@implementation UIView(ForUIImageViewSwizzle)

+ (void)load {

    Method originMethod = class_getInstanceMethod([UIView class], @selector(setContentMode:));

    Method swizzledMethod = class_getInstanceMethod([UIView class], @selector(nty_setContentMode:));

    method_exchangeImplementations(originMethod, swizzledMethod);

}

- (void)nty_setContentMode:(UIViewContentMode)contentMode {

    if ([self isKindOfClass:[UIImageView class]]) {

      // 執(zhí)行針對(duì)UIImageView的swizzle的邏輯

    }

    [self nty_setContentMode:contentMode];

}

@end

見(jiàn)圖8

圖8

由于兩個(gè)分類的swizzle名字相同,通過(guò)class_getInstanceMethod獲得nty_setContentMode的Method將一直是同一個(gè)(該問(wèn)題出現(xiàn)原因需要詳細(xì)了解class翘县、category實(shí)現(xiàn)機(jī)制最域,此處不多做綴述),所以相當(dāng)于兩個(gè)Method互相swizzle了兩次锈麸,最終SEL與IMP的連接仍為圖8的結(jié)果镀脂。

示例6

將示例5的代碼做一點(diǎn)點(diǎn)調(diào)整,將UIView(ForUIImageViewSwizzle)中替換nty_setContentMode方法名改為nty2_setContentMode

見(jiàn)圖9?10?11

圖9
圖10
圖11

最終成功完成需求

Swizzle在項(xiàng)目中應(yīng)用出現(xiàn)的問(wèn)題

iOS項(xiàng)目在很多方法中如果傳參不對(duì)忘伞,會(huì)直接導(dǎo)致crash薄翅。比如NSString的substringToIndex:方法在數(shù)組越界時(shí)沙兰、NSDictionary傳入nil值時(shí)、NSArray數(shù)組越界時(shí)翘魄。這些情況鼎天,我們可能用swizzle將這些系統(tǒng)方法進(jìn)行swizzle,加入數(shù)據(jù)空值暑竟、數(shù)組越界情況的容錯(cuò)處理斋射,有效減少崩潰率。

此處但荤,以NSString的substringToIndex:方法為例罗岖。

示例1


@implementation NSString (AvoidCrash)

+ (void)load {

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        [[NSString class] jr_swizzleMethod:@selector(substringToIndex:) withMethod:@selector(nty_substringToIndex:) error:nil];

    };

}

- (NSString*)nty_substringToIndex:(NSUInteger)to {

    if (to <= self.length) {

        return [self nty_substringToIndex:to];

    }

    return self;

}

@end

在Demo中寫(xiě)下測(cè)試代碼測(cè)試此功能


- (void)testCrash {

    NSString *testStr = @"asdf";

    [testStr substringToIndex:100];

}

然后,崩潰了腹躁,發(fā)現(xiàn)此swizzle方法完全沒(méi)有被調(diào)用桑包。

類簇

類簇 是一群隱藏在通用接口下的與實(shí)現(xiàn)相關(guān)的類,使得我們編寫(xiě)的代碼可以獨(dú)立于底層實(shí)現(xiàn)(因?yàn)榻涌谑欠€(wěn)定的)纺非。

示例2

將代碼改成如下形式


+ (void)load {

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        Class clazz = nil;

        id obj;

        /* 普通方法 */

        obj = [[NSString alloc] init];

        clazz = [obj class];

        [obj release];

        ACSwizzle(clazz,substringToIndex:);

    });

}

然而哑了,根據(jù)友盟上統(tǒng)計(jì)的crash結(jié)果,仍有substringToIndex導(dǎo)致的崩潰問(wèn)題烧颖。

示例3

示例2的崩潰問(wèn)題是由于弱左,不同形式聲明的NSString產(chǎn)生的類簇有可能不同。為避免此問(wèn)題倒信,寫(xiě)了一個(gè)Demo去讀取出不同NSString聲明方式會(huì)出現(xiàn)的所有類科贬。

2017-12-26 15:19:39.378849+0800 TestClassType[3787:1570162] [NSString alloc] 's class is NSPlaceholderString

2017-12-26 15:19:39.378881+0800 TestClassType[3787:1570162] [[NSString alloc] init] 's class is __NSCFConstantString

2017-12-26 15:19:39.378896+0800 TestClassType[3787:1570162] @"as" 's class is __NSCFConstantString

2017-12-26 15:19:39.378908+0800 TestClassType[3787:1570162] @"" 's class is __NSCFConstantString

2017-12-26 15:19:39.378918+0800 TestClassType[3787:1570162] @"as".copy 's class is __NSCFConstantString

2017-12-26 15:19:39.378942+0800 TestClassType[3787:1570162] ([NSString stringWithFormat:@"aa%@", @"a"]) 's class is NSTaggedPointerString

2017-12-26 15:19:39.378998+0800 TestClassType[3787:1570162] [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] 's class is NSTaggedPointerString

2017-12-26 15:19:39.379032+0800 TestClassType[3787:1570162]

然后將所有的類簇都進(jìn)行swizzle


+ (void)load {

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        /* 普通方法 */

        NSArray *classNameList = @[

                                  @"__NSCFConstantString",

                                  @"NSTaggedPointerString"

                                  ];

        for (NSString *className in classNameList) {

            Class clazz = NSClassFromString(className);

            if (clazz) {

                [clazz jr_swizzleMethod:@selector(substringToIndex:) withMethod:@selector(nty_substringToIndex:) error:nil];

            }

        }

    });

}

經(jīng)運(yùn)行,發(fā)生了iOS 8設(shè)備100%崩潰無(wú)法使用的問(wèn)題鳖悠。

示例4

將自己查詢類簇的Demo在iOS 8設(shè)備上運(yùn)行榜掌,導(dǎo)出如下結(jié)果

2017-12-26 15:16:37.673 TestClassType[389:48818] [NSString alloc] 's class is NSPlaceholderString

2017-12-26 15:16:37.673 TestClassType[389:48818] [[NSString alloc] init] 's class is __NSCFConstantString

2017-12-26 15:16:37.673 TestClassType[389:48818] @"as" 's class is __NSCFConstantString

2017-12-26 15:16:37.674 TestClassType[389:48818] @"" 's class is __NSCFConstantString

2017-12-26 15:16:37.674 TestClassType[389:48818] @"as".copy 's class is __NSCFConstantString

2017-12-26 15:16:37.674 TestClassType[389:48818] ([NSString stringWithFormat:@"aa%@", @"a"]) 's class is __NSCFString

2017-12-26 15:16:37.674 TestClassType[389:48818] [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] 's class is __NSCFString

2017-12-26 15:16:37.674 TestClassType[389:48818]

發(fā)現(xiàn)在iOS 8設(shè)備上,沒(méi)有NSTaggedPointerString這種類型乘综,如果對(duì)NSTaggedPointerString進(jìn)行swizzle憎账,就會(huì)出現(xiàn)崩潰。

于是卡辰,想出一種復(fù)雜的判斷各因素的方法胞皱,它將會(huì)考慮NSString不同聲明形式的類簇的排重問(wèn)題,NSString與NSMutableString的類的相同類簇的排重問(wèn)題


@implementation NSMutableString (AvoidCrash)

+ (void)load {

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        id obj = [NSMutableString alloc];

        Class clazz;

        NSData*data      = [@"testdata" dataUsingEncoding:NSUTF8StringEncoding];

        NSArray *varList = @[

            [[[NSString alloc] init] autorelease],

            @"as",

            @"",

            @"as".copy,

            [NSString stringWithFormat:@"aa%@", @"a"],

            [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]

        ];

        NSArray *mutaVarList = @[

            [[[NSMutableString alloc] init] autorelease],

            @"as".mutableCopy,

            @"".mutableCopy,

            [NSMutableString stringWithString:@"as"],

            [[[NSMutableString alloc] initWithString:@"as"] autorelease],

            [[[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]

        ];

        [self swizzleForVarList:varList

                    mutaVarList:mutaVarList

                      varBlock:^(Class clazz) {

                [clazz jr_swizzleMethod:@selector(substringToIndex:) withMethod:@selector(nty_substringToIndex:) error:nil];

        } mutaVarBlock:^(Class clazz) {

                [clazz jr_swizzleMethod:@selector(substringToIndex:) withMethod:@selector(nty_substringToIndex:) error:nil];

        }];

    });

}

- (void)swizzleForVarList:(NSArray*)varList

              mutaVarList:(NSArray*)mutaVarList

                varBlock:(void (^)(Class clazz))varSwizzleBlock

            mutaVarBlock:(void (^)(Class clazz))mutaVarSwizzleBlock {

    // 使用Set九妈,保證數(shù)據(jù)去重

    NSMutableSet *mutaClassList = [NSMutableSet set];

    NSMutableSet *classList    = [NSMutableSet set];

    for (NSString *var in mutaVarList) {

        // 將MutableXXX的變量轉(zhuǎn)成類名存入mutaClassList

        [mutaClassList addObject:[var class]];

    }

    for (NSString *var in varList) {

        // 將XXX的變量轉(zhuǎn)成類名存入classList

        [classList addObject:[var class]];

    }

    for (Class clazz in mutaClassList) {

        // 遍歷MutableXXX類簇的各種隱藏子類反砌,進(jìn)行swizzle

        if (mutaVarSwizzleBlock) {

            mutaVarSwizzleBlock(clazz);

        }

    }

    for (Class clazz in classList) {

        // 有時(shí)MutableXXX與XXX類簇中的隱藏子類有相同的(比如NSString與NSMutableString都有__NSCFString)

        // 此處確保不會(huì)被swizzle兩處

        if (![mutaClassList containsObject:clazz]

            && varSwizzleBlock) {

            varSwizzleBlock(clazz);

        }

    }

}

@end

此時(shí),無(wú)明顯的問(wèn)題萌朱。但在編寫(xiě)Unit Test遍歷各種錯(cuò)誤情況時(shí)宴树,發(fā)現(xiàn)@"sa"這種形式的NSString在執(zhí)行數(shù)組越界時(shí)仍會(huì)崩潰。

經(jīng)分析晶疼,@"sa"形式的類簇是__NSCFConstantString酒贬。而__NSCFConstantString的父類是__NSCFString又憨。__NSCFConstantString的substringToIndex方法是實(shí)現(xiàn)在__NSCFString中的。此處就會(huì)發(fā)生父類锭吨、子類兩次swizzle引起的問(wèn)題蠢莺,導(dǎo)致__NSCFConstantString的substringToIndex方法仍指向系統(tǒng)方法的IMP。

Demo5

而我們很難去識(shí)別類簇之間是否有繼承關(guān)系零如,而繼承關(guān)系的類簇的方法是否是只在父類中實(shí)現(xiàn)躏将。

所以最終,對(duì)避免crash想使用的高級(jí)辯別類簇的功能全線失敗埠况。我們使用簡(jiǎn)單的網(wǎng)絡(luò)上歸納好的類簇進(jìn)行swizzle耸携,并對(duì)這些方法進(jìn)行了詳進(jìn)的Unit Test編寫(xiě)測(cè)試棵癣。最終發(fā)現(xiàn)辕翰, 此化繁為簡(jiǎn)的方法,能夠完美的解決所有問(wèn)題狈谊。


/* 普通方法 */

        // iOS 8是__NSCFConstantString喜命,iOS 11上是__NSCFConstantString

        id obj = [[NSString alloc] init];

        Class clazz = [obj class];

        [clazz jr_swizzleMethod:@selector(substringToIndex:) withMethod:@selector(nty_substringToIndex:) error:nil];

        // iOS 8上是__NSCFString, iOS 11上是NSTaggedPointerString

        id obj2 = [NSString stringWithFormat:@"aa%@", @"a"];

        if (![obj2 isKindOfClass:clazz]

            && ![obj isKindOfClass:[obj2 class]]) {

            // 若obj2與obj的類簇不同且不是繼承關(guān)系河劝,則進(jìn)行swizzle

            // (__NSCFConstantString的父類是__NSCFString)

            clazz = [obj2 class];

            [clazz jr_swizzleMethod:@selector(substringToIndex:) withMethod:@selector(nty_substringToIndex:) error:nil];

        }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末壁榕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赎瞎,更是在濱河造成了極大的恐慌牌里,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件务甥,死亡現(xiàn)場(chǎng)離奇詭異牡辽,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)敞临,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)态辛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人挺尿,你說(shuō)我怎么就攤上這事奏黑。” “怎么了编矾?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵熟史,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我窄俏,道長(zhǎng)蹂匹,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任裆操,我火速辦了婚禮怒详,結(jié)果婚禮上炉媒,老公的妹妹穿的比我還像新娘。我一直安慰自己昆烁,他們只是感情好吊骤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著静尼,像睡著了一般白粉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鼠渺,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天鸭巴,我揣著相機(jī)與錄音,去河邊找鬼拦盹。 笑死鹃祖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的普舆。 我是一名探鬼主播恬口,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼沼侣!你這毒婦竟也來(lái)了祖能?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蛾洛,失蹤者是張志新(化名)和其女友劉穎养铸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體轧膘,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钞螟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扶供。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筛圆。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖椿浓,靈堂內(nèi)的尸體忽然破棺而出太援,到底是詐尸還是另有隱情,我是刑警寧澤扳碍,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布提岔,位于F島的核電站,受9級(jí)特大地震影響笋敞,放射性物質(zhì)發(fā)生泄漏碱蒙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赛惩。 院中可真熱鬧哀墓,春花似錦、人聲如沸喷兼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)季惯。三九已至吠各,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間勉抓,已是汗流浹背贾漏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留藕筋,地道東北人纵散。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像念逞,于是被迫代替她去往敵國(guó)和親困食。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345