Objective-C:探索block(二)

Objective-C:探索block(一)中簡單地說了下block的基本上面貌百框,包括它的語法和底層定義骤宣,了解block的底層定義對在項(xiàng)目開發(fā)中正確使用block極為重要诞帐。
本篇文章要探索block使用中不可避免的幾個(gè)方面

  1. 基本類型變量與對象的截取
  2. __block修飾符
  3. block存儲(chǔ)域
  4. copy的使用
  5. 相互引用問題

一. 基本數(shù)據(jù)類型變量與對象的截取

1.block截獲基本數(shù)據(jù)類型變量

在這小節(jié)會(huì)解答:為什么給定義在代碼體外面的局部變量重新賦值會(huì)引起編譯錯(cuò)誤
下面是block截取基本數(shù)據(jù)類型的示例代碼:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int aInteger = 3;
        char *str = "hello world";
        void (^myprint)(void) = ^void(void){
            printf("%s-%d\n",str,aInteger);
        };
        myprint();
    }
    return 0;
}

注意:由于控制臺標(biāo)準(zhǔn)輸出有緩存淘正,當(dāng)有行刷新標(biāo)志或者行緩存已滿時(shí)百侧,系統(tǒng)才會(huì)把緩存數(shù)據(jù)輸出到控制臺蜻势,Xcode8編譯printf打印函數(shù)結(jié)束加上\n換行符勺三,后臺才有打印輸出

示例代碼中,代碼塊myprint的代碼體中使用了變量aInteger代态,str,使用終端命令反編譯:clang -rewrite-objc main.m, 可以在main.cpp文件的底部看到代碼塊myprint的底層定義疹吃,主要有:

//1.存儲(chǔ)與block實(shí)現(xiàn)相關(guān)的信息
struct __block_impl {
  void *isa;  //指向block結(jié)構(gòu)體實(shí)例所在的內(nèi)存區(qū)域
  int Flags;  //系統(tǒng)默認(rèn)值為0
  int Reserved; //構(gòu)造方法里沒看到賦值蹦疑,用來存儲(chǔ)block保留內(nèi)存空間大小
  void *FuncPtr; //指向block代碼體實(shí)現(xiàn)的函數(shù)指針,block的調(diào)用關(guān)鍵就它來尋址了
};
//2.此結(jié)構(gòu)體記錄block的描述信息萨驶,它在定義時(shí)順便初始化了個(gè)實(shí)例__main_block_desc_0_DATA
static struct __main_block_desc_0 {
  size_t reserved; //指明block在內(nèi)存中要保留一塊內(nèi)存空間的大小歉摧,這塊內(nèi)存區(qū)暫沒用途。
  size_t Block_size;//指明block的結(jié)構(gòu)體實(shí)例的大写勰臁:sizeof(struct __main_block_impl_0) 
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

//3.代碼塊myprint的真身判莉,就是一個(gè)結(jié)構(gòu)體
struct __main_block_impl_0 {
  struct __block_impl impl; //存儲(chǔ)與block實(shí)現(xiàn)(代碼體)相關(guān)的信息
  struct __main_block_desc_0* Desc;//存儲(chǔ)block的描述信息
  char *str;  //截獲到的str變量
  int aInteger; //截獲到的aInteger變量
  //結(jié)構(gòu)體的構(gòu)造方法
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, char *_str, int _aInteger, int flags=0) : str(_str), aInteger(_aInteger) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
//4.block代碼體的實(shí)現(xiàn)函數(shù),就是一個(gè)C函數(shù)育谬,可以通過函數(shù)指針來調(diào)用,傳入的參數(shù)為block的結(jié)構(gòu)體實(shí)例本身
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//編譯器在編譯過程中會(huì)在block代碼體的實(shí)現(xiàn)函數(shù)中券盅,聲明與截獲的外部局部變量名稱相同的局部變量,并進(jìn)行賦值
   char *str = __cself->str; // cself為block的結(jié)構(gòu)體實(shí)例本身
   int aInteger = __cself->aInteger; // 
   
   printf("%s%d\n",str,aInteger);
}

//程序入口膛檀,主函數(shù)
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    //這兩個(gè)變量雖然與block的結(jié)構(gòu)體實(shí)例的成員變量名稱相同锰镀,但根本是不同,所以在block代碼體的實(shí)現(xiàn)函數(shù)里給它們賦值咖刃,會(huì)編譯報(bào)錯(cuò):Variable is not assignable,因?yàn)槌隽俗兞康淖饔糜?        int aInteger = 3;
        char *str = "hello world";
   
   /*-----------重點(diǎn)-----------*/
       //1.void (*myprint)(void):聲明一個(gè)返回類型為空泳炉,參數(shù)為空,名稱叫myprint的函數(shù)指針,它指向了block的構(gòu)造方法,構(gòu)造方法將創(chuàng)建一個(gè)block結(jié)構(gòu)體實(shí)例
        void (*myprint)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, str, aInteger));

        //2.(void (*)(__block_impl *))這是一個(gè)返回值為void嚎杨,參數(shù)類型為__block_impl *的指針類型花鹅,用來修飾實(shí)例myprint的成員變量FuncPtr(即myprint->FuncPtr), ((__block_impl *)myprint)為FuncPtr的參數(shù)
        ((void (*)(__block_impl *))((__block_impl *)myprint)->FuncPtr)((__block_impl *)myprint);
    }

//類型轉(zhuǎn)換簡化后枫浙,1和2相當(dāng)于下面:
struct __main_block_impl_0 tmp = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA,str,aInteger);
struct __main_block_impl_0 *myprint = &tmp;
(*myprint->impl.FuncPtr)(myprint);
  /*-----------重點(diǎn)-----------*/

    return 0;
}

顯然刨肃,代碼塊myprint經(jīng)過編譯后,代碼塊myprint底層結(jié)構(gòu)體會(huì)新增成員變量aInteger,str并在構(gòu)造函數(shù)中進(jìn)行初始化箩帚。同時(shí)可以看到代碼塊myprint的實(shí)現(xiàn)函數(shù)static void __main_block_func_0(struct __main_block_impl_0 *__cself)中重新聲明了相同名稱的變量int aInteger和char *str,此時(shí)的aInteget真友,str與聲明在主函數(shù)的局部變量是兩個(gè)完全沒關(guān)系的不同變量,它們不在同一作用域(同一函數(shù))并且編譯器有它自己的一套編譯規(guī)則(1.支持截獲變量的瞬間值紧帕,2.不支持在代碼體內(nèi)給外部的局部變量賦值盔然,因?yàn)椴辉试S在block的實(shí)現(xiàn)函數(shù)里給main函數(shù)的局部變量賦值,盡管兩個(gè)變量名稱相同),這也解釋為什么給定義在代碼體外面的局部變量重新賦值會(huì)引起編譯錯(cuò)誤愈案。想要給aInteger,str重新賦值挺尾,可以把它們聲明為全局變量或靜態(tài)變量來解決作用域問題,但一般不選擇這樣做刻帚,而是在它們前面加上修飾符__block潦嘶,這樣就可以通過編譯(將在第二小節(jié)探索)。

2.block截獲對象

下面是block截取對象的oc源代碼:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        {
            NSMutableArray *array = [[NSMutableArray alloc] init];
            void(^myprint)(void)=^{
                printf("數(shù)組Count=%ld\n",array.count);
            };
            myprint();
        }
    }
    return 0;
}

示例中崇众,myprint截獲了可變數(shù)組array掂僵,以下是轉(zhuǎn)換成C++的代碼:

//代碼塊myprint的結(jié)構(gòu)體
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  NSMutableArray *array; //截獲的array
  //代碼塊myprint結(jié)構(gòu)體的構(gòu)造方法
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableArray *_array, int flags=0) : array(_array) {
    impl.isa = &_NSConcreteStackBlock; //isa指向結(jié)構(gòu)體實(shí)例本身,實(shí)例分配在棧區(qū)
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

//myprint代碼體對應(yīng)的實(shí)現(xiàn)函數(shù)
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  NSMutableArray *array = __cself->array; 
  //array.count反編譯成runtime的消息發(fā)送objc_msgSend(arry,@selector(count));
   printf("數(shù)組Count=%ld\n",((NSUInteger (*)(id, SEL))(void *)objc_msgSend)((id)array, sel_registerName("count")));
 }
 
//拷貝操作函數(shù)顷歌,
//1.這個(gè)函數(shù)用來拷貝myprint結(jié)構(gòu)體實(shí)例的array對象
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    _Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
//廢棄操作函數(shù)锰蓬,廢棄myprint結(jié)構(gòu)體實(shí)例的array對象,相當(dāng)對象realse
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

//存儲(chǔ)代碼塊myprint的結(jié)構(gòu)體的描述信息并實(shí)例化一個(gè)實(shí)例__main_block_desc_0_DATA
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  //函數(shù)指針指向拷貝操作函數(shù)
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  //函數(shù)指針指向廢棄操作函數(shù)
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

//主函數(shù)
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        {
 //(NSMutableArray *(*)(id, SEL))把objc_msgSend(id,SEL)轉(zhuǎn)成返回值為NSMutableArray *,參數(shù)為id,SEL的函數(shù)指針。
 //簡化如下:
 NSMutableArray *aZone = objc_msgSend(objc_getClass("NSMutableArray"),sel_registerName("alloc"));
 NSMutableArray *array = objc_msgSend(aZone,sel_registerName("alloc"));
 // 
             NSMutableArray *array = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("alloc")), sel_registerName("init"));
             
            void(*myprint)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, array, 570425344));
            ((void (*)(__block_impl *))((__block_impl *)myprint)->FuncPtr)((__block_impl *)myprint);

        }

    }
    return 0;
}

由此可知眯漩,編譯器對block截獲對象與截獲變量的處理基本一樣:在block的結(jié)構(gòu)體中生成一個(gè)與截獲的變量名稱相同的成員變量芹扭。與截獲基本數(shù)據(jù)類型變量不同的是,截獲對象時(shí)赦抖,編譯器會(huì)生成block_copyblock_dispose 函數(shù)用于對象的內(nèi)存管理舱卡。

二. __block修飾符

1.編譯器對__block的處理

我們知道在block代碼體中不能給截獲的變量重新賦值,但可以用__block來修飾被截獲的變量队萤,再進(jìn)行重新賦值轮锥,示例如下:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
           __block int aInteger = 1;
            void(^myprint)(void)=^{
                aInteger = 3;
                printf("aInteger=%d\n",aInteger);
            };
            myprint();
    }
    return 0;
}

為了給block截獲的變量重新賦值,可使用__block修飾符要尔,下面是編譯器對加了__block修飾符的變量的處理舍杜,與上面反編譯后的代碼相差不大:

//編譯器會(huì)為被__block修飾的變量生成一個(gè)結(jié)構(gòu)體類型,
struct __Block_byref_aInteger_0 {
  void *__isa; 
__Block_byref_aInteger_0 *__forwarding; //__forwarding指向__block變量結(jié)構(gòu)體實(shí)例本身
 int __flags;
 int __size;
 int aInteger; //與外部的局部變量名稱相同的成員變量
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_aInteger_0 *aInteger; // 截獲的__block變量aInteger
  //block結(jié)構(gòu)體的構(gòu)造函數(shù)
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_aInteger_0 *_aInteger, int flags=0) : aInteger(_aInteger->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;  
    impl.Flags = flags;
    impl.FuncPtr = fp;  //函數(shù)指針指向block代碼體的實(shí)現(xiàn)函數(shù)
    Desc = desc;
  }
};
//block代碼體的實(shí)現(xiàn)函數(shù)赵辕,以block的結(jié)構(gòu)實(shí)例本身作為參數(shù)傳遞
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//結(jié)構(gòu)體aInteger的__forwarding指向自身既绩,繞了一步再取成員變量int aInteger
  __Block_byref_aInteger_0 *aInteger = __cself->aInteger; 
  (aInteger->__forwarding->aInteger) = 3;
  printf("aInteger=%d\n",(aInteger->__forwarding->aInteger));
 }
//block變量的copy函數(shù)用于對象的內(nèi)存管理  (拷貝對象,引用計(jì)數(shù)+1)         
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->aInteger, (void*)src->aInteger, 8/*BLOCK_FIELD_IS_BYREF*/);
}
//block變量的dispose函數(shù)用于對象的內(nèi)存管理 (廢棄對象)
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->aInteger, 8/*BLOCK_FIELD_IS_BYREF*/);}

//存儲(chǔ)block的描述信息还惠,在聲明同時(shí)實(shí)例化一個(gè)__main_block_desc_0_DATA實(shí)例
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); 
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

//主函數(shù)
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        {
        //實(shí)例化aInteger結(jié)構(gòu)體
            __attribute__((__blocks__(byref))) __Block_byref_aInteger_0 aInteger = {
(void*)0 //void *isa
(__Block_byref_aInteger_0 *)&aInteger, //__forwardind
 0, 
  sizeof(__Block_byref_aInteger_0), 
  1
  };

//函數(shù)指針myprint指向myprint代碼體的構(gòu)造函數(shù)即指向了結(jié)構(gòu)體實(shí)例            
  void(*myprint)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_aInteger_0 *)&aInteger, 570425344));

//下面去掉類型轉(zhuǎn)換:(myprint->FuncPtr)(myprint),即結(jié)構(gòu)體實(shí)例myprint引用成員變量FuncPtr指針饲握,來調(diào)用block代碼體的實(shí)現(xiàn)函數(shù)__main_block_func_0
((void (*)(__block_impl *))((__block_impl *)myprint)->FuncPtr)((__block_impl *)myprint);

        }
    }
    return 0;
}

2.對象的內(nèi)容可以進(jìn)行操作

不能對沒有__block修飾符修飾的變量進(jìn)行重新賦值,但截獲的變量如果是對象類型的話蚕键,是可以對其內(nèi)容進(jìn)行操作的互拾,例如:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:2];
        void(^myprint)(void)=^{
               [mArray addObject:@"Hello"];
            printf("mArray.count=%ld\n",mArray.count);
            };
        myprint();
    }
    return 0;
}

三. block存儲(chǔ)域

下面是內(nèi)存分配的一些說明:
一:聲明在函數(shù)內(nèi)部的局部變量,系統(tǒng)自動(dòng)分配到棧上嚎幸,且在函數(shù)執(zhí)行結(jié)束后,被釋放寄猩。
二:由程序員調(diào)用alloc,malloc,new嫉晶,copy等方法初始化的變量,會(huì)分配到堆上,程序員可手動(dòng)調(diào)用free來釋放替废。否則箍铭,等程序結(jié)束后,由系統(tǒng)釋放回收
三:一些靜態(tài)變量椎镣,全局變量诈火,常量會(huì)在編譯階段分配在數(shù)據(jù) 區(qū)(data區(qū))
四:操作指令會(huì)分配到文本區(qū)(text區(qū))
以下是一個(gè)應(yīng)用在內(nèi)存中進(jìn)程空間的布局(線程thread共享進(jìn)程的內(nèi)存空間)


這里寫圖片描述

1.分配在棧上的block

聲明在函數(shù)內(nèi)部的block代碼體會(huì)分配在棧上。
在上面的c++代碼中状答,block的結(jié)構(gòu)體實(shí)例構(gòu)造方法會(huì)給impl.isa賦值為 &_NSConcreteStackBlock冷守,我們知道isa指針是指向了block結(jié)構(gòu)體實(shí)例自身,即&_NSConcreteStackBlock指向了block結(jié)構(gòu)體實(shí)例在內(nèi)存中的起址地址惊科,NSConcreteStackBlock拍摇,見文知義,Stack是棧的意思馆截,可以推測系統(tǒng)將block的結(jié)構(gòu)體實(shí)例分配在棧上充活。

2.分配在堆上的block

當(dāng)block代碼體賦值給擁有strong,__strong蜡娶,copy修飾的變量時(shí)混卵,block會(huì)在堆上生成一份副本,并將副本賦值給變量窖张,block結(jié)構(gòu)體實(shí)例的impl.isa=&_NSConcreteMallocBlock幕随,isa指針指向堆上的實(shí)例

3.分配在數(shù)據(jù)區(qū)(全局)的block

在全局地方聲明的block代碼體(在@interface外面聲明),會(huì)分配在數(shù)據(jù)區(qū)荤堪,impl.isa=&_NSConcreteGlobalBlock 指向數(shù)據(jù)區(qū)合陵。

四.copy的使用

1.為什么要用copy呢?

一般情況下澄阳,block的代碼體會(huì)聲明在某個(gè)方法內(nèi)而極少聲明在@interface外面(可理解為全局的地方拥知,因數(shù)在全局地方block截獲不了有用的變量),我們知道碎赢,在方法內(nèi)聲明的(不是由all,new初始化的)會(huì)分配到棧上低剔,并且會(huì)在方法執(zhí)行完結(jié)時(shí)被釋放,這樣就極容易引用了被釋放的對象肮塞,從而拋出內(nèi)存讀取錯(cuò)誤的異常襟齿,所以會(huì)為block聲明一個(gè)copy屬性,使它指向堆上的對象以確保其生命周期枕赵。

在項(xiàng)目開發(fā)猜欺,為某個(gè)類聲明一個(gè)block屬性時(shí),通常會(huì)這樣寫:

typdef void (^Myblock)(void)
@interface MyClass
@property (copy,nonatomic) Myblock oneBlock;
...
{
    self.oneBlock=^{ NSLog(@"Hello world!"); };
}

當(dāng)為屬性指明copy屬性時(shí)拷窜,^{ NSLog(@"Hello world!"); } 的結(jié)構(gòu)體實(shí)例將被從棧上copy一份副本到堆上开皿,且_oneBlock指向了堆上的副本涧黄。其實(shí),聲明onBlock屬性也可以這樣寫 @property (strong,nonatomic) Myblock oneBlock; 或者@property (nonatomic) Myblock oneBlock; 因?yàn)閷傩詓trong作用相當(dāng)于修飾符__strong(__strong是對象的隱式說明)赋荆,編譯器會(huì)為__strong的對象分配到堆上作內(nèi)存管理笋妥。但如果非要作死,寫成@property (weak,nonatomic) Myblock oneBlock; 窄潭,就會(huì)報(bào)錯(cuò)了bad_address_access春宣。

2.不用手動(dòng)copy的情況

在一些情況下,出于內(nèi)存管理的原因嫉你,編譯器會(huì)自動(dòng)幫我們copy, 不我們自己去copy,情況如下:
一:block作為函數(shù)的返回值被傳遞時(shí)月帝,如

-(OneBlock)someMethod{
  OneBlock aBlock = ^{...};
  return aBlock; //不需要寫成return [aBlock copy];
}

二:Cocoa框架方法或GCD的API方法中含有usingBlock來傳遞block時(shí)
三:將block賦值給帶有__strong修飾符的id類型的類或__block說明符的變量。

五.相互引用問題

由于block會(huì)截獲出現(xiàn)在代碼體的外部對象(定義在代碼體{}外部的對象)均抽,并在block的結(jié)構(gòu)體內(nèi)聲明一個(gè)與之名稱相同的成員變量并在實(shí)例化時(shí)指向并截獲的外部對象嫁赏,如果被截獲的對象又擁有或間接擁有(持有)該block,就會(huì)形成閉環(huán)油挥,引起相互引用的問題潦蝇,不能被有效釋放。

1.相互引用

引起循環(huán)引用的示例如下:

#import "MyClass.h"
typedef void(^Myprint)(void);
@interface MyClass ()
{
    Myprint _myprint;
}
@end
@implementation MyClass
-(void)myPrintMethod{
    _myprint = ^{
        NSLog(@"%@",self); //編譯器警告:Capturing 'self'strongly in this block is likely to lead to a retain cycle
    };
}
@end

2.間接相互引用

#import "MyClass.h"
typedef void(^Myprint)(void);
@interface MyClass ()
{
    Myprint _myprint;
}
@property(strong,nonatomic) NSString *str;

@end
@implementation MyClass
-(void)myPrintMethod{
    _myprint = ^{
        NSLog(@"%@",_str);//編譯器警告:Capturing 'self'strongly in this block is likely to lead to a retain cycle
    };
}
@end

3.__weak解決相互引用

形成相互引用的兩個(gè)對象深寥,不能被有效釋放攘乒,dealloc方法也不會(huì)執(zhí)行。


這里寫圖片描述

A對象要釋放惋鹅,首先要釋放B则酝,B要釋放,那么A對象就要先釋放闰集,這樣會(huì)形成死鎖沽讹,導(dǎo)致A,B對象都不能釋放。__weak修飾符或__unsafe_unretained修飾符可解決循環(huán)引用問題武鲁,示例如下:

#import "MyClass.h"
typedef void(^Myprint)(void);
@interface MyClass ()
{
    Myprint _myprint;
}
@end
@implementation MyClass
-(void)myPrintMethod{
    __weak typeof(self) weakSelf = self; //__weak修飾符讓weak可引用self,但并不持有self
    _myprint = ^{
        NSLog(@"%@",weakSelf);
    };
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末爽雄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子沐鼠,更是在濱河造成了極大的恐慌挚瘟,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饲梭,死亡現(xiàn)場離奇詭異乘盖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)憔涉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門订框,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人兜叨,你說我怎么就攤上這事布蔗√傥ィ” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵纵揍,是天一觀的道長。 經(jīng)常有香客問我议街,道長泽谨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任特漩,我火速辦了婚禮吧雹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涂身。我一直安慰自己雄卷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布蛤售。 她就那樣靜靜地躺著丁鹉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悴能。 梳的紋絲不亂的頭發(fā)上揣钦,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音漠酿,去河邊找鬼冯凹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛炒嘲,可吹牛的內(nèi)容都是我干的宇姚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼夫凸,長吁一口氣:“原來是場噩夢啊……” “哼浑劳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起寸痢,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤呀洲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后啼止,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體道逗,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年献烦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了滓窍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡巩那,死狀恐怖吏夯,靈堂內(nèi)的尸體忽然破棺而出此蜈,到底是詐尸還是另有隱情,我是刑警寧澤噪生,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布裆赵,位于F島的核電站,受9級特大地震影響跺嗽,放射性物質(zhì)發(fā)生泄漏战授。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一桨嫁、第九天 我趴在偏房一處隱蔽的房頂上張望植兰。 院中可真熱鬧,春花似錦璃吧、人聲如沸楣导。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筒繁。三九已至,卻和暖如春朦促,著一層夾襖步出監(jiān)牢的瞬間膝晾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工务冕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留血当,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓臊旭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親箩退。 傳聞我的和親對象是個(gè)殘疾皇子离熏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

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