iOS Block Part7:block^內(nèi)存管理

參考文檔1:BlocksRuntime/runtime.c
參考文檔2:BlocksRuntime/Block_private.h
參考文檔3:BlocksRuntime/Block.h

理解block的拷貝只是開(kāi)始,
1.block(int any),block(NSString * any),block(__block int any),block(__block NSString * any)真的就這四類block嗎?
2.iOS Block Part6所提到的block拷貝是棧block拷貝生成堆block的過(guò)程,那么對(duì)全局block進(jìn)行拷貝會(huì)有什么效果?對(duì)堆block進(jìn)行拷貝又會(huì)有什么效果?block的內(nèi)存到底如何管理?
3.block如何造成循環(huán)引用?
4.如何避免?為什么用weak修飾的self就不會(huì)產(chǎn)生循環(huán)引用?
......諸如此類的問(wèn)題.
本系列文章也只是拋磚引玉,很多東西,還得自己去看去理解!

1. 四小類block,其實(shí)有些狹隘

/*******************************************************

Entry points used by the compiler - the real API!


A Block can reference four different kinds of things that require help when the Block is copied to the heap.
1) C++ stack based objects
2) References to Objective-C objects
3) Other Blocks
4) __block variables

In these cases helper functions are synthesized by the compiler for use in Block_copy and Block_release, called the copy and dispose helpers.  The copy helper emits a call to the C++ const copy constructor for C++ stack based objects and for the rest calls into the runtime support function _Block_object_assign.  The dispose helper has a call to the C++ destructor for case 1 and a call into _Block_object_dispose for the rest.

The flags parameter of _Block_object_assign and _Block_object_dispose is set to
    * BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object,
    * BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and
    * BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable.
If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16).

So the Block copy/dispose helpers should only ever generate the four flag values of 3, 7, 8, and 24.

When  a __block variable is either a C++ object, an Objective-C object, or another Block then the compiler also generates copy/dispose helper functions.  Similarly to the Block copy helper, the "__block" copy helper (formerly and still a.k.a. "byref" copy helper) will do a C++ copy constructor (not a const one though!) and the dispose helper will do the destructor.  And similarly the helpers will call into the same two support functions with the same values for objects and Blocks with the additional BLOCK_BYREF_CALLER (128) bit of information supplied.

So the __block copy/dispose helpers will generate flag values of 3 or 7 for objects and Blocks respectively, with BLOCK_FIELD_IS_WEAK (16) or'ed as appropriate and always 128 or'd in, for the following set of possibilities:
    __block id                   128+3
        __weak block id              128+3+16
    __block (^Block)             128+7
    __weak __block (^Block)      128+7+16
        
The implementation of the two routines would be improved by switch statements enumerating the eight cases.

以上是BlocksRuntime/runtime.c內(nèi)的一段話.
很容易看出,block捕獲的外圍參數(shù)絕不僅限于int any,NSString * any,__block int any,__block NSString * any這四種.
本系列文章未深入解析這部分內(nèi)容的原因是:文字陳述功力有限,不敢越雷池.
就以上面提到的3) Other Blocks為例子:
block捕獲block,這內(nèi)存關(guān)系,臣妾做不到啊!所以筆者只寫出基本的四類.想了解更復(fù)雜類型block的內(nèi)存關(guān)系,得自己對(duì)block的原理有了一定了解后,自己再看源碼.

2.block的內(nèi)存管理

void *_Block_copy(const void *arg) {
    return _Block_copy_internal(arg, WANTS_ONE);
}
static void *_Block_copy_internal(const void *arg, const int flags) {
    struct Block_layout *aBlock;
    const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;

    //printf("_Block_copy_internal(%p, %x)\n", arg, flags);
    if (!arg) return NULL;

    // The following would be better done as a switch statement
    aBlock = (struct Block_layout *)arg;
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock;
    }

    // Its a stack block.  Make a copy.
    if (!isGC) {
        struct Block_layout *result = malloc(aBlock->descriptor->size);
        if (!result) return (void *)0;
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
        // reset refcount
        result->flags &= ~(BLOCK_REFCOUNT_MASK);    // XXX not needed
        result->flags |= BLOCK_NEEDS_FREE | 1;
        result->isa = _NSConcreteMallocBlock;
        if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
            //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);
            (*aBlock->descriptor->copy)(result, aBlock); // do fixup
        }
        return result;
    }
}

回到block拷貝調(diào)用的_Block_copy_internal,前篇文章已經(jīng)說(shuō)了,棧block拷貝生成堆block只用了這個(gè)方法內(nèi)的部分代碼.

  • 對(duì)全局block進(jìn)行拷貝會(huì)怎么樣
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
     return aBlock;
}

返回原來(lái)的block

  • 對(duì)堆block進(jìn)行拷貝會(huì)怎么樣
堆block的flags為什么是BLOCK_NEEDS_FREE,棧block拷貝生成堆block的時(shí)候有賦值,很容易看明白,不多解釋

if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
      latching_incr_int(&aBlock->flags);
      return aBlock;
}

latching_incr_int加引用計(jì)數(shù).
在看block的結(jié)構(gòu):

//Block_private.h內(nèi)Block_layout的結(jié)構(gòu):
struct Block_layout {
    void *isa;
    int flags;
    int reserved; 
    void (*invoke)(void *, ...);
    struct Block_descriptor *descriptor;
    /* Imported variables. */
};

//編譯的結(jié)構(gòu):
struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
};

很容易看出:
Block_layout = __main_block_impl_0與__block_impl的合二為一

既有isa又有引用計(jì)數(shù)(記在flags內(nèi)),所以block也是一個(gè)對(duì)象.block是存在還是銷毀全看block的引用計(jì)數(shù).

_Block_copy,就有_Block_release.
latching_incr_int,就有latching_decr_int.
相生相克,無(wú)休無(wú)止,有興趣可以在BlocksRuntime/runtime.c里看看.

3.造成循環(huán)引用

3.1引用對(duì)象
NSString * any = [NSString stringWithFormat:@"1"];
void (^test)() = ^ {
    NSLog(@"%@",any);
};
test();
block(NSString)_map.png
block捕獲參數(shù)的拷貝方法

void _Block_object_assign(void *destAddr, const void *object, const int flags) {
   .
   .
   .
    else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
        //printf("retaining object at %p\n", object);
        _Block_retain_object(object);//變量加引用計(jì)數(shù)
        //printf("done retaining object at %p\n", object);
        _Block_assign((void *)object, destAddr);
    }
}

由上圖很明顯能看出,由block編譯生成的結(jié)構(gòu)體__main_block_impl_0是如何持有NSString * any的.
再加上代碼,我也能看到,持有變量的引用計(jì)數(shù)相應(yīng)加1.

所以block銷毀,NSString * any才有可以釋放.

3.2循環(huán)引用

ViewController內(nèi)使用block來(lái)說(shuō)明問(wèn)題.
一概而論,block寫了self就會(huì)造成循環(huán)引用肯定是錯(cuò)的.
比如在ViewController寫了如下代碼:

[UIView animateWithDuration:0.5 animations:^{
     [self doSomethings];
}];

無(wú)論block內(nèi)的代碼執(zhí)行與否.ViewController的銷毀都會(huì)正常.

那怎么才會(huì)造成循環(huán)引用呢?
所謂循環(huán)引用多數(shù)情況下都是多方在內(nèi)存上構(gòu)成了一個(gè)引用閉環(huán).

  • 持有型Block

在日常開(kāi)發(fā)中,用Block做消息傳遞十分常見(jiàn).請(qǐng)看一下代碼:

@interface ZCOnView : UIView
@property(nonatomic,copy)void (^clickMarkBlock)(BOOL isMarked);
@end

ZCOnView * any = [[ZCOnView alloc]initWithFrame:CGRectMake(64.0, 64.0, 64.0, 64.0)];
any.backgroundColor = [UIColor redColor];
any.clickMarkBlock = ^(BOOL isMarked){
   [self doSomethings];
};
[self.view addSubview:any];
VC與UI控件關(guān)系不必多言--強(qiáng)
Block被UI控件持有,UI控件存在,Block也就存在--強(qiáng)
Block持有VC--強(qiáng)
       VC
    /      \
   /        \
UI控件------Block
引用閉環(huán)形成.

在以上代碼的Block內(nèi)寫上self(或者用了成員變量),肯定會(huì)造成循環(huán)引用.所以我稱這種Block持有型Block.

  • 使用型Block

再反觀代碼:

[UIView animateWithDuration:0.5 animations:^{
     [self doSomethings];
}];
VC在堆上
Block在堆上

Block執(zhí)行完畢,Block銷毀==>Block不持有VC.
沒(méi)有形成引用閉環(huán),當(dāng)然就沒(méi)有循環(huán)引用.
我們姑且稱這類block使用型Block吧!

Block可以分為持有型Block+使用型Block.持有型Block內(nèi)部肯定不能用self.而使用型Block內(nèi)部可以放心的使用self.才怪!!!(?? 使用型Block內(nèi)部用self,雖然不會(huì)造成循環(huán)引用,但會(huì)有Block使用時(shí)的另一個(gè)問(wèn)題==>延遲.本文就不細(xì)說(shuō)了.想了解請(qǐng)戳)

4.避免循環(huán)引用

4.1 ARC環(huán)境下

區(qū)分Block類型再看看能不能用self.如果覺(jué)得麻煩,一竿子打死也行,全用__weak __typeof(&*self)weakSelf = self也可以.
那么weak修飾的變量又是如何躲過(guò)循環(huán)引用的呢?

環(huán)境:ARC
命令:clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations AnyThing.m

  • strong self
#import <Foundation/Foundation.h>
@interface AnyThing : NSObject
@end

#import "AnyThing.h"
@implementation AnyThing
- (void)someAct{
    void (^test)() = ^ {
        NSLog(@"%@",self);
    };
    test();
}
@end
// @implementation AnyThing


struct __AnyThing__someAct_block_impl_0 {
    struct __block_impl impl;
    struct __AnyThing__someAct_block_desc_0* Desc;
    AnyThing *const __strong self;//<<<不同
    __AnyThing__someAct_block_impl_0(void *fp, struct __AnyThing__someAct_block_desc_0 *desc, AnyThing *const __strong _self, int flags=0) : self(_self) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

static void __AnyThing__someAct_block_func_0(struct __AnyThing__someAct_block_impl_0 *__cself) {
    AnyThing *const __strong self = __cself->self; // bound by copy
    
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_qp_p2pj3jmj65n39jgl4wx9_l9w0000gn_T_AnyThing_51e757_mi_0,self);
}

static void __AnyThing__someAct_block_copy_0(struct __AnyThing__someAct_block_impl_0*dst, struct __AnyThing__someAct_block_impl_0*src) {
_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __AnyThing__someAct_block_dispose_0(struct __AnyThing__someAct_block_impl_0*src) {
_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static struct __AnyThing__someAct_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __AnyThing__someAct_block_impl_0*, struct __AnyThing__someAct_block_impl_0*);
    void (*dispose)(struct __AnyThing__someAct_block_impl_0*);
} __AnyThing__someAct_block_desc_0_DATA = { 0, sizeof(struct __AnyThing__someAct_block_impl_0), __AnyThing__someAct_block_copy_0, __AnyThing__someAct_block_dispose_0};

static void _I_AnyThing_someAct(AnyThing * self, SEL _cmd) {
 void (*test)() = ((void (*)())&__AnyThing__someAct_block_impl_0((void *)__AnyThing__someAct_block_func_0, &__AnyThing__someAct_block_desc_0_DATA, self, 570425344));
 ((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test);
}

// @end
  • weak self
#import <Foundation/Foundation.h>
@interface AnyThing : NSObject
@end

#import "AnyThing.h"
@implementation AnyThing
- (void)someAct{
    __weak __typeof(&*self)weakSelf = self;
    void (^test)() = ^ {
        NSLog(@"%@",weakSelf);
    };
    test();
}
@end
// @implementation AnyThing


struct __AnyThing__someAct_block_impl_0 {
    struct __block_impl impl;
    struct __AnyThing__someAct_block_desc_0* Desc;
    __weak typeof (&*self) weakSelf;//<<<不同
    __AnyThing__someAct_block_impl_0(void *fp, struct __AnyThing__someAct_block_desc_0 *desc, __weak typeof (&*self) _weakSelf, int flags=0) : weakSelf(_weakSelf) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

static void __AnyThing__someAct_block_func_0(struct __AnyThing__someAct_block_impl_0 *__cself) {
    __weak typeof (&*self) weakSelf = __cself->weakSelf; // bound by copy
    
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_qp_p2pj3jmj65n39jgl4wx9_l9w0000gn_T_AnyThing_fda013_mi_0,weakSelf);
}

static void __AnyThing__someAct_block_copy_0(struct __AnyThing__someAct_block_impl_0*dst, struct __AnyThing__someAct_block_impl_0*src) {
_Block_object_assign((void*)&dst->weakSelf, (void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __AnyThing__someAct_block_dispose_0(struct __AnyThing__someAct_block_impl_0*src) {
_Block_object_dispose((void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static struct __AnyThing__someAct_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __AnyThing__someAct_block_impl_0*, struct __AnyThing__someAct_block_impl_0*);
    void (*dispose)(struct __AnyThing__someAct_block_impl_0*);
} __AnyThing__someAct_block_desc_0_DATA = { 0, sizeof(struct __AnyThing__someAct_block_impl_0), __AnyThing__someAct_block_copy_0, __AnyThing__someAct_block_dispose_0};

static void _I_AnyThing_someAct(AnyThing * self, SEL _cmd) {
 __attribute__((objc_ownership(weak))) __typeof(&*self)weakSelf = self;
 void (*test)() = ((void (*)())&__AnyThing__someAct_block_impl_0((void *)__AnyThing__someAct_block_func_0, &__AnyThing__someAct_block_desc_0_DATA, weakSelf, 570425344));
 ((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test);
}

// @end

根據(jù)上面的不同,知道:

strong
struct __AnyThing__someAct_block_impl_0對(duì)self保持強(qiáng)引用
weak
struct __AnyThing__someAct_block_impl_0對(duì)self保持弱引用

strong weak 對(duì)捕獲對(duì)象的拷貝,傳入flag一模一樣

static void __AnyThing__someAct_block_copy_0(struct __AnyThing__someAct_block_impl_0*dst, struct __AnyThing__someAct_block_impl_0*src) {
_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
static void __AnyThing__someAct_block_copy_0(struct __AnyThing__someAct_block_impl_0*dst, struct __AnyThing__someAct_block_impl_0*src) {
_Block_object_assign((void*)&dst->weakSelf, (void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
void _Block_object_assign(void *destAddr, const void *object, const int flags) {
    //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags);
    ......
    else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
        //printf("retaining object at %p\n", object);
        _Block_retain_object(object);//加引用計(jì)數(shù)?
        //printf("done retaining object at %p\n", object);
        _Block_assign((void *)object, destAddr);
    }
}

strong self 加引用計(jì)數(shù),可以理解!
weak self 加引用計(jì)數(shù),是不是有些矛盾?

不要奇怪,ARC環(huán)境有了更完善的內(nèi)存管理禽绪,如果外部變量由__strong怎静、copy胁孙、strong修飾時(shí)告组,Block會(huì)把捕獲的變量用__strong來(lái)修飾進(jìn)而達(dá)到持有的目的.這里的_Block_retain_object只不過(guò)是一個(gè)空操作.
以下代碼見(jiàn)于BlocksRuntime/runtime.c

static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
static void _Block_retain_object_default(const void *ptr __unused) {
}
4.2 MRC環(huán)境下

在一些文章里也看到過(guò),在MRC環(huán)境下,用__block修飾對(duì)象也能避免循環(huán)引用.那我們也通過(guò)編譯后的代碼來(lái)看一下其內(nèi)在原理.

注意上面提到過(guò)的_Block_retain_object方法,在ARC中,它是一個(gè)空操作.
在MRC中,_Block_retain_object可不是一個(gè)空控制,_Block_retain_object會(huì)給傳入的對(duì)象加引用計(jì)數(shù).

iOS Block Part5里,我們已經(jīng)知道,在ARC環(huán)境下__block修飾對(duì)象,會(huì)有以下對(duì)對(duì)象的拷貝動(dòng)作.

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
131==BLOCK_FIELD_IS_OBJECT||BLOCK_FIELD_IS_CALLER;

而MRC環(huán)境下也是一樣的.
所以在_Block_object_assign內(nèi)走的是以下代碼片段,很好的避開(kāi)了給對(duì)象加引用計(jì)數(shù)的_Block_retain_object方法,所以也就不會(huì)有循環(huán)引用.

void _Block_object_assign(void *destAddr, const void *object, const int flags) {
    //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags);
    if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
        if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
            _Block_assign_weak(object, destAddr);
        }
        else {
            // do *not* retain or *copy* __block variables whatever they are
            _Block_assign((void *)object, destAddr);
        }
    }
    ...
}

5.拋磚引玉

雖然我已經(jīng)仔細(xì)的檢查了自己的相關(guān)代碼和相關(guān)的措辭忌怎,但是請(qǐng)不要盲目相信本文的正確性。
我已經(jīng)見(jiàn)過(guò)非常多的經(jīng)驗(yàn)開(kāi)發(fā)者對(duì)于 Block 有錯(cuò)誤的理解(我也不會(huì)例外)吼砂。請(qǐng)一定保持一顆懷疑的心绩鸣。
by 酷酷的哀殿

關(guān)于Block的認(rèn)識(shí)文章改了許多遍,每回都以為自己已經(jīng)得其精髓,但一次一次證明自己還是太淺薄.本系列文章也只是拋磚引玉,很多東西,還得自己去看去理解!


參考文獻(xiàn):
Block技巧與底層解析 by tripleCC

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市凡简,隨后出現(xiàn)的幾起案子逼友,更是在濱河造成了極大的恐慌,老刑警劉巖秤涩,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帜乞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡筐眷,警方通過(guò)查閱死者的電腦和手機(jī)黎烈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)匀谣,“玉大人照棋,你說(shuō)我怎么就攤上這事∥漪幔” “怎么了烈炭?”我有些...
    開(kāi)封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)宝恶。 經(jīng)常有香客問(wèn)我符隙,道長(zhǎng),這世上最難降的妖魔是什么卑惜? 我笑而不...
    開(kāi)封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任膏执,我火速辦了婚禮驻售,結(jié)果婚禮上露久,老公的妹妹穿的比我還像新娘。我一直安慰自己欺栗,他們只是感情好毫痕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布征峦。 她就那樣靜靜地躺著,像睡著了一般消请。 火紅的嫁衣襯著肌膚如雪栏笆。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天臊泰,我揣著相機(jī)與錄音蛉加,去河邊找鬼。 笑死缸逃,一個(gè)胖子當(dāng)著我的面吹牛针饥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播需频,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼丁眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了昭殉?” 一聲冷哼從身側(cè)響起苞七,我...
    開(kāi)封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挪丢,沒(méi)想到半個(gè)月后蹂风,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吃靠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年硫眨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巢块。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡礁阁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出族奢,到底是詐尸還是另有隱情姥闭,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布越走,位于F島的核電站棚品,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏廊敌。R本人自食惡果不足惜铜跑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骡澈。 院中可真熱鬧锅纺,春花似錦、人聲如沸肋殴。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至官地,卻和暖如春酿傍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背驱入。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工赤炒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亏较。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓可霎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親宴杀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子癣朗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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