RunLoop解決卡頓問題

漫步時(shí)光

人生就像RunLoop料皇,不斷的循環(huán)谓松、不斷的往復(fù)。當(dāng)線程被殺掉践剂,當(dāng)生命結(jié)束鬼譬,RunLoop就消失了,人生也就結(jié)束了逊脯。在有限的生命里优质,為何不讓自己像RunLoop一樣優(yōu)雅的活著,享受每一個(gè)循環(huán)军洼。

作為一個(gè)有強(qiáng)迫癥患者巩螃,在自己的app中容不下一絲雜質(zhì)。最近了解了一下Runloop,在這里更大家分享下匕争。

這里有一個(gè)模擬卡頓的Demo避乏,包含卡頓與優(yōu)化后的代碼,雖然工作中很少遇到卡頓,(有人會(huì)說就算卡頓也是因?yàn)榇a沒寫好,是的甘桑,我這里只是模擬一個(gè)卡頓的現(xiàn)象并通過Runloop解決拍皮,重點(diǎn)是Runloop解決問題的方式歹叮,不是這個(gè)代碼不該卡頓):

目錄
通過在storyboard中更改兩套代碼

一、卡頓分析

最根本的原因是RunLoop轉(zhuǎn)一圈的時(shí)間太長了春缕,因?yàn)橐淮蜶unLoop循環(huán)需要解析24張大圖盗胀,很卡
既然一次RunLoop加載24張圖卡艘蹋,那能不能一次循環(huán)加載1張呢锄贼?

二、分析RunLoop的運(yùn)行機(jī)制

RunLoop:運(yùn)行循環(huán)
-保證(線程)不退出
-負(fù)責(zé)監(jiān)聽所有事件:時(shí)鐘女阀、觸摸宅荤、網(wǎng)絡(luò)事件,沒有事件就睡眠
-每一條線程上面都有一個(gè)RunLoop浸策,但是子線程的RunLoop默認(rèn)不運(yùn)行
RunLoop的事件處理:每當(dāng)有時(shí)鐘冯键、觸摸、網(wǎng)絡(luò)事件發(fā)生的時(shí)候庸汗,RunLoop蘇醒惫确,執(zhí)行一次循環(huán),循環(huán)執(zhí)行完畢馬上進(jìn)入睡眠狀態(tài)蚯舱。
可以猜想:能不能通過NSTimer來每間隔一定時(shí)間執(zhí)行一個(gè)任務(wù)改化,這樣RunLoop每間隔一定時(shí)間就會(huì)蘇醒一次。每蘇醒一次就執(zhí)行加載一張圖片枉昏。當(dāng)圖片加載完成讓NSTimer釋放陈肛,當(dāng)列表每滑動(dòng)一次,讓NSTimer重新執(zhí)行任務(wù)兄裂。

如下圖句旱,RunLoop 想要跑起來,必須有 Mode 對象支持晰奖,而 Mode 里面必須有
(NSSet *)Source谈撒、 (NSArray *)Timer ,源和定時(shí)器匾南。
至于另外一個(gè)類(NSArray *)observer是用于監(jiān)聽 RunLoop 的狀態(tài)港华,因此不會(huì)激活RunLoop。


Runloop

observer是用于監(jiān)聽 RunLoop 的狀態(tài)午衰,我們就可以通過observer來監(jiān)聽runloop的蘇醒

偽代碼
1立宜、創(chuàng)建一個(gè)定時(shí)器:每間隔0.001s執(zhí)行一個(gè)空方法來喚醒RunLoop
2、將加載圖片的方法裝入block臊岸,將block加入數(shù)組
3橙数、監(jiān)聽RunLoop的蘇醒,蘇醒回掉就執(zhí)行一次就從數(shù)組中取出一個(gè)事件帅戒,執(zhí)行完的事件從數(shù)組中刪除

說干就干灯帮。

三崖技、代碼實(shí)現(xiàn)

可以先下載Demo

1、創(chuàng)建一個(gè)定時(shí)器:每間隔0.001s執(zhí)行一個(gè)空方法來喚醒RunLoop(這里存在質(zhì)疑钟哥,后面已經(jīng)回答了質(zhì)疑)

self.timer = [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];

-(void)timerMethod{
    //啥都不干!!
}

(這里要感謝DreamTracer大神Q提的建議)
那這里添加定時(shí)器又是為了干什么呢迎献?應(yīng)不應(yīng)該加呢?下面會(huì)講到腻贰。

2吁恍、將加載圖片的方法裝入block,將block加入數(shù)組

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyTableViewCell *cell = [[MyTableViewCell alloc] cellWithTableView:tableView withID:@"cell"];
    NSLog(@"current:%ld",(long)indexPath.row);
    ...
    cell.myImageView.image = nil;
    cell.secondLImage.image = nil;
    cell.thirdyLImage.image = nil;
    cell.fouthLImage.image = nil;
   ##### //添加事件
    //添加文字
    [self addTask:^{
        cell.myLabel.text = [NSString stringWithFormat:@"%zd - Drawing index is top priority", indexPath.row];
    }];
    [self addTask:^{
        cell.thirdLabel.text = [NSString stringWithFormat:@"%zd - Drawing large image is low priority. Should be distributed into different run loop passes.", indexPath.row];
    }];
    
    NSString *path1 = [[NSBundle mainBundle] pathForResource:@"spaceship" ofType:@"png"];
    
    //添加圖片
    [self addTask:^{
        UIImage *image1 = [UIImage imageWithContentsOfFile:path1];
        
        cell.myImageView.image = image1;
    }];
    
    [self addTask:^{
        UIImage *image2 = [UIImage imageWithContentsOfFile:path1];
        
        cell.secondLImage.image = image2;
        
    }];
    [self addTask:^{
        UIImage *image3 = [UIImage imageWithContentsOfFile:path1];
        
        cell.thirdyLImage.image = image3;
    }];
    [self addTask:^{
        UIImage *image4 = [UIImage imageWithContentsOfFile:path1];
        
        cell.fouthLImage.image = image4;
    }];
    return cell;
}
-(void)addTask:(RunloopBlock)task{
    if (!self.timer) {//這是優(yōu)化
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
    }
    
    //添加任務(wù)到數(shù)組!!
    [self.tasks addObject:task];
    
    
}

3播演、監(jiān)聽RunLoop的蘇醒冀瓦,蘇醒回掉就執(zhí)行一次就從數(shù)組中取出一個(gè)事件,執(zhí)行完的事件從數(shù)組中刪除

#pragma mark - <RunLoop>
//添加RunLoop觀察者!!  CoreFoundtion 里面 Ref (引用)指針!!
-(void)addRunloopObserver{
    //拿到當(dāng)前的runloop
    CFRunLoopRef runloop = CFRunLoopGetCurrent();
    //定義一個(gè)context
    CFRunLoopObserverContext context = {
        0,
        (__bridge void *)(self),
        &CFRetain,
        &CFRelease,
        NULL,
    };
    
    //定義觀察
    static CFRunLoopObserverRef defaultModeObserver;
    //創(chuàng)建觀察者
    defaultModeObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &Callback, &context);
    //添加當(dāng)前runloop的觀察者!!
    CFRunLoopAddObserver(runloop, defaultModeObserver, kCFRunLoopCommonModes);
    //C 語言里面Create相關(guān)的函數(shù)!創(chuàng)建出來的指針!需要釋放
    CFRelease(defaultModeObserver);
}

static void Callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    NSLog(@"gemelaile ");
    //拿到控制器
    ViewController * vc = (__bridge ViewController *)info;
    if (vc.tasks.count == 0) {//任務(wù)執(zhí)行完成就清掉timer
        [vc.timer invalidate];
        vc.timer = nil;
        return;
    }
    RunloopBlock task = vc.tasks.firstObject;
    task();
    //干掉第一個(gè)任務(wù)
    [vc.tasks removeObjectAtIndex:0];
}

如上代碼做到了監(jiān)聽RunLoop的蘇醒写烤,每次蘇醒都會(huì)回掉Callback方法

4 打開demo翼闽,分別引入

demo

ViewController(優(yōu)化過的)與ViewControllerNo(未優(yōu)化過的)運(yùn)行,看是不是完美解決卡頓洲炊。(可以看看內(nèi)存感局,cpu)

敲黑板

那么我們現(xiàn)在來講講為什么前面我們要添加定時(shí)器。
我們把加載圖片的事件放進(jìn)數(shù)組中暂衡,每次runloop循環(huán)一次就執(zhí)行一次事件询微。每次有拖動(dòng)事件發(fā)生,runloop都會(huì)自動(dòng)執(zhí)行古徒,runloop執(zhí)行幾次呢拓提,我不知道。所以為了安全起見隧膘,這里我加了個(gè)定時(shí)器代态。當(dāng)然這里可以添加優(yōu)化,例如滑動(dòng)結(jié)束后初始化定時(shí)器疹吃,事件執(zhí)行完就清理定時(shí)器蹦疑。

完美解決卡頓問題,RunLoop是不是很強(qiáng)大萨驶。
是不是以為這篇文章就結(jié)束了歉摧,那你就太小看我了。
每次有拖動(dòng)事件發(fā)生腔呜,runloop都會(huì)自動(dòng)執(zhí)行叁温,runloop執(zhí)行幾次呢,我不知道核畴。最后我進(jìn)入了深入的實(shí)驗(yàn)了解膝但。

真正的重點(diǎn)來了

    //創(chuàng)建runloop的即將處理 Source的觀察者
    static CFRunLoopObserverRef defaultModeObserver1;
    defaultModeObserver1 = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeSources, YES, 0, & sourceTodo, &context);
    //添加當(dāng)前runloop的觀察者!!
    CFRunLoopAddObserver(runloop, defaultModeObserver1, kCFRunLoopCommonModes);
    //C 語言里面Create相關(guān)的函數(shù)!創(chuàng)建出來的指針!需要釋放
    CFRelease(defaultModeObserver1);


static void sourceTodo(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    NSLog(@"sourceTodo");
}

這樣每次有事件就會(huì)調(diào)用sourceTodo的事件
在callback也加上輸出log,同時(shí)把[vc.tasks removeObjectAtIndex:0];注銷掉,那樣vc.tasks就一直有事件谤草,看看到底callBack會(huì)走多少次跟束。這樣是不是就解決了我們的疑惑呢

static void Callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    
    
    //拿到控制器
    ViewController * vc = (__bridge ViewController *)info;
    NSLog(@"CallbackNoTask");//這里還沒有執(zhí)行事件
    if (vc.tasks.count == 0) {
        return;
    }
    RunloopBlock task = vc.tasks.firstObject;
    task();
    //干掉第一個(gè)任務(wù)
   // [vc.tasks removeObjectAtIndex:0];
    NSLog(@"CallbackHasTask");//這里執(zhí)行了事件
    
}

一個(gè)驚人的發(fā)現(xiàn)莺奸,callback一直停不下來。這是為什么?

RunloopBlock task = vc.tasks.firstObject;
task();

task()執(zhí)行了什么冀宴,難道里面包含source或timer事件

+(void)addImage1With:(UITableViewCell *)cell{
    //第一張
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(5, 20, 85, 85)];
    imageView.tag = 1;
    NSString *path1 = [[NSBundle mainBundle] pathForResource:@"spaceship" ofType:@"png"];
    UIImage *image = [UIImage imageWithContentsOfFile:path1];
    imageView.contentMode = UIViewContentModeScaleAspectFit;
    imageView.image = image;
    [UIView transitionWithView:cell.contentView duration:0.3 options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionCrossDissolve) animations:^{
        [cell.contentView addSubview:imageView];
    } completion:nil];
}
[UIView transitionWithView:cell.contentView duration:0.3 options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionCrossDissolve) animations:^{
        [cell.contentView addSubview:imageView];
    } completion:nil];

這是一個(gè)timer事件
那這樣果斷去掉定時(shí)器灭贷。
把注銷的代碼[vc.tasks removeObjectAtIndex:0]打開,運(yùn)行看看

事件執(zhí)行完后略贮,runloop還是會(huì)跑幾次就結(jié)束了甚疟。
功夫不負(fù)苦心人,終于算了解決了這個(gè)疑惑刨肃。再次感謝之前對我的文章提出質(zhì)疑的大神們古拴,是你們讓我有了動(dòng)力來解決這些疑惑箩帚。

有沒有了解的欲望U嬗选!紧帕!
完整的Demo

重點(diǎn)在runloop的用法盔然,不在為什么會(huì)卡頓

下面是RunLoop的一些基礎(chǔ)知識(shí),希望對你有幫助

RunLoop入門

一是嗜、簡介

首先愈案,先象征性的講下RunLoop的概念
從字面上看,就可以看出就是兜圈圈鹅搪,就是一個(gè)死循環(huán)嘛站绪。

二、作用

1.保持程序運(yùn)行
2.處理app的各種事件(比如觸摸丽柿,定時(shí)器等等)
3.節(jié)省CPU資源恢准,提高性能。

三甫题、枯燥知識(shí)

下面是關(guān)于RunLoop的一些使用簡述馁筐。也許有點(diǎn)枯燥,但是也是必須要知道的W狗恰(敲黑板ing)敏沉,我盡量說的通俗易懂一點(diǎn)。

1.兩個(gè)API

首先要知道iOS里面有兩套API可以訪問和使用RunLoop:

Foundation

NSRunLoop

Core Foundation

CFRunLoopRef

上面兩套都可以使用炎码,但是要知道CFRunLoopRef是用c語言寫的盟迟,是開源的,相比于NSRunLoop更加底層潦闲,而NSRunLoop其實(shí)是對CFRunLoopRef的一個(gè)簡單的封裝攒菠。便于使用而已。這樣說來矫钓,顯然CFRunLoopRef的性能要高一點(diǎn)要尔。

2.RunLoop與線程(形象)

1.每條線程都有唯一的與之對應(yīng)的RunLoop對象舍杜。
2.主線程的RunLoop已經(jīng)創(chuàng)建好了,而子線程的需要手動(dòng)創(chuàng)建赵辕。(也就是說子線程的RunLoop默認(rèn)是關(guān)閉的既绩,因?yàn)橛袝r(shí)候開了個(gè)線程但卻沒有必要開一個(gè)RunLoop,不然反而浪費(fèi)了資源还惠。 )
3.RunLoop在第一次獲取時(shí)創(chuàng)建饲握,在線程結(jié)束時(shí)銷毀。(這就相當(dāng)于 線程是一個(gè)類蚕键,RunLoop是類里的實(shí)例變量救欧,這樣便于理解)

3.獲取RunLoop對象

Foundation

[NSRunLoop currentRunLoop]; // 獲得當(dāng)前線程的RunLoop對象
[NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對象

Core Foundation

CFRunLoopGetCurrent(); // 獲得當(dāng)前線程的RunLoop對象
CFRunLoopGetMain(); // 獲得主線程的RunLoop對象 

4.RunLoop相關(guān)類

在Core Foundation中有RunLoop的五個(gè)類

    CFRunLoopRef
    CFRunLoopModeRef
    CFRunLoopSourceRef
    CFRunLoopTimerRef
    CFRunLoopObserverRef

這五個(gè)類的關(guān)系如下

五個(gè)類的關(guān)系

由圖中可以得出以下幾點(diǎn):
1.CFRunLoopModeRef代表的是RunLoop的運(yùn)行模式。
2.一個(gè) RunLoop 包含若干個(gè) Mode锣光,每個(gè) Mode 又包含若干個(gè) Source/Timer/Observer笆怠。
3.每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode誊爹,這個(gè)Mode被稱作 CurrentMode蹬刷。
4.如果需要切換 Mode,只能退出 Loop频丘,再重新指定一個(gè) Mode 進(jìn)入办成。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響搂漠。

CFRunLoopModeRef

系統(tǒng)默認(rèn)注冊了5個(gè)mode

kCFRunLoopDefaultMode //App的默認(rèn)Mode迂卢,通常主線程是在這個(gè)Mode下運(yùn)行
UITrackingRunLoopMode //界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng)桐汤,保證界面滑動(dòng)時(shí)不受其他 Mode 影響
UIInitializationRunLoopMode // 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode而克,啟動(dòng)完成后就不再使用
GSEventReceiveRunLoopMode // 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
kCFRunLoopCommonModes //這是一個(gè)占位用的Mode惊科,不是一種真正的Mode

至于CFRunLoopModeRef的使用我會(huì)在 下面的實(shí)驗(yàn)三timer的使用中 詳細(xì)說到拍摇。

四、實(shí)驗(yàn)講解

這里開始之前馆截,希望您跟著新建一個(gè)工程充活。實(shí)操最清晰。
一蜡娶、main函數(shù)的實(shí)驗(yàn)
再來做個(gè)試驗(yàn):將main的代碼添加一個(gè)輸出NSLog混卵,如下

int main(int argc, char * argv[]) {
    @autoreleasepool {
        int res = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        NSLog(@"-----");
        return res;

    }
}

你猜會(huì)輸出 “-----” 嗎?答案是否定的窖张,你會(huì)發(fā)現(xiàn)程序始終不會(huì)到NSLog(@"-----");這一行來幕随。這就說明了程序一直在運(yùn)行著。其實(shí)這都是RunLoop的功勞宿接,它的其中一個(gè)功能就是保持程序的持續(xù)運(yùn)行。有了RunLoop,main里面相當(dāng)于是這樣的代碼(偽代碼):

BOOL running = YES;
do {
   // 執(zhí)行各種操作
} while (running);
return 0;

程序是始終在while里面的退渗,是一個(gè)死循環(huán)。
說到這里你肯定又會(huì)疑惑走诞,RunLoop是什么時(shí)候創(chuàng)建的。其實(shí)在UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]))這個(gè)函數(shù)的內(nèi)部就已經(jīng)啟動(dòng)了一個(gè)RunLoop蛤高,所以函數(shù)一直沒有返回蚣旱,這才使得程序保持運(yùn)行。
(注意:這個(gè)默認(rèn)啟動(dòng)的RunLoop是和主線程相關(guān)的!!!)

二戴陡、NSTimer的使用

在項(xiàng)目中用的NSTimer其實(shí)也和RunLoop有關(guān)系塞绿,下面我們來做個(gè)實(shí)驗(yàn)

實(shí)驗(yàn)一 scheduledTimer方法

新建一個(gè)工程,在ViewController中添加一個(gè)UIButton恤批,增加button的響應(yīng)以及timerTest方法异吻,代碼如下

- (IBAction)ButtonDidClick:(id)sender {
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
}

- (void)timerTest
{
    NSLog(@"timerTest----");
}

點(diǎn)擊button可以看到輸出臺(tái)每隔一秒鐘就打印"timerTest----"。

實(shí)驗(yàn)二 timerWithTime方法

代碼如下:

- (IBAction)ButtonDidClick:(id)sender {
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
}

- (void)timerTest
{
    NSLog(@"timerTest----");
}

但是實(shí)驗(yàn)結(jié)果是开皿,點(diǎn)擊button后沒有反應(yīng)涧黄。為什么呢篮昧?
噢~原來是少加了一句話赋荆,添加后的代碼如下:

- (IBAction)ButtonDidClick:(id)sender {
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}

- (void)timerTest
{
    NSLog(@"timerTest----");
}

可是,為什么實(shí)驗(yàn)二比實(shí)驗(yàn)一要多加一句話呢懊昨?解:那是因?yàn)閟cheduledTimer方法會(huì)自動(dòng)添加到當(dāng)前的runloop里面去窄潭,而且runloop的運(yùn)行模式kCFRunLoopDefaultMode,也就是說實(shí)驗(yàn)一已經(jīng)將timer自動(dòng)加入到了一個(gè)運(yùn)行模式為kCFRunLoopDefaultMode的runloop中酵颁。

實(shí)驗(yàn)三 有scrollView的情況下使用Timer

首先嫉你,按鈕響應(yīng)以及timerTest的方法如下:

- (IBAction)ButtonDidClick:(id)sender {
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}

- (void)timerTest
{
    NSLog(@"timerTest----");
}

然后在vc中加一個(gè)textView,run起來躏惋,

然后點(diǎn)擊按鈕幽污,隨后滑動(dòng)textView,根據(jù)打印結(jié)果可以看出滑動(dòng)textView的時(shí)候是不打印的簿姨,奇怪吧距误。其實(shí)說到底還是RunLoop搞的鬼”馕唬可以看到准潭,我們把timer加到了NSDefaultRunLoopMode的runLoop中,而在滑動(dòng)textview的時(shí)候域仇,RunLoop就切換到UITrackingRunLoopMode模式刑然,而上面有提到說:在每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode暇务,這個(gè)Mode被稱作 CurrentMode泼掠。 所以定時(shí)器就不起作用了怔软。
現(xiàn)在可以思考一下解決方法了!(敲黑板ing)
提示一下择镇,問題出在了模式上面爽雄,是不是修改一下模式就好了呢。

解決方法:
上面有提到過五個(gè)mode

    kCFRunLoopDefaultMode //App的默認(rèn)Mode沐鼠,通常主線程是在這個(gè)Mode下運(yùn)行
    UITrackingRunLoopMode //界面跟蹤 Mode挚瘟,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
    UIInitializationRunLoopMode // 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode饲梭,啟動(dòng)完成后就不再使用
    GSEventReceiveRunLoopMode // 接受系統(tǒng)事件的內(nèi)部 Mode乘盖,通常用不到
    kCFRunLoopCommonModes //這是一個(gè)占位用的Mode,不是一種真正的Mode

其實(shí)如果把mode改為kCFRunLoopCommonModes的話就可以既支持kCFRunLoopDefaultMode又支持UITrackingRunLoopMode了憔涉。
修改如下:
修改mode類型

- (IBAction)ButtonDidClick:(id)sender {
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

然后run發(fā)現(xiàn)就算滾動(dòng)textView也不會(huì)影響打印订框。

寫在最后:
希望這篇文章對您有幫助。當(dāng)然如果您發(fā)現(xiàn)有可以優(yōu)化的地方兜叨,希望您能慷慨的提出來穿扳。最后祝您工作愉快!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末国旷,一起剝皮案震驚了整個(gè)濱河市矛物,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跪但,老刑警劉巖履羞,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異屡久,居然都是意外死亡忆首,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門被环,熙熙樓的掌柜王于貴愁眉苦臉地迎上來糙及,“玉大人,你說我怎么就攤上這事筛欢〗牵” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵悴能,是天一觀的道長揣钦。 經(jīng)常有香客問我,道長漠酿,這世上最難降的妖魔是什么冯凹? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上宇姚,老公的妹妹穿的比我還像新娘匈庭。我一直安慰自己,他們只是感情好浑劳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布阱持。 她就那樣靜靜地躺著,像睡著了一般魔熏。 火紅的嫁衣襯著肌膚如雪衷咽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天蒜绽,我揣著相機(jī)與錄音镶骗,去河邊找鬼。 笑死躲雅,一個(gè)胖子當(dāng)著我的面吹牛鼎姊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播相赁,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼相寇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了钮科?” 一聲冷哼從身側(cè)響起唤衫,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎跺嗽,沒想到半個(gè)月后战授,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桨嫁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了份帐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片璃吧。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖废境,靈堂內(nèi)的尸體忽然破棺而出畜挨,到底是詐尸還是另有隱情,我是刑警寧澤噩凹,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布巴元,位于F島的核電站,受9級特大地震影響驮宴,放射性物質(zhì)發(fā)生泄漏逮刨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一堵泽、第九天 我趴在偏房一處隱蔽的房頂上張望修己。 院中可真熱鬧恢总,春花似錦、人聲如沸睬愤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尤辱。三九已至砂豌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間光督,已是汗流浹背奸鸯。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留可帽,地道東北人娄涩。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像映跟,于是被迫代替她去往敵國和親蓄拣。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

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