老司機(jī)出品————多線程實(shí)踐

老司機(jī)WickyIP屬地: 云南
0.254字?jǐn)?shù) 1,984
多線程實(shí)踐

有段時(shí)間沒寫博客了,不過這也不是一次兩次了。

厚顏無恥

嗯,就不找理由也不檢討了,直奔主題吧。

在今天的博客中你將會(huì)看到:

  • 異步線程同步
  • NSOperation子類重寫
  • 條件模塊
  • 請求類封裝

異步線程同步

老司機(jī)今天講的不是多線程的基本用法,這個(gè)東西往上的博客其實(shí)蠻多的,而且也基本是多線程的基本用法。老司機(jī)今天主要的是介紹多個(gè)異步線程執(zhí)行結(jié)束后進(jìn)行回調(diào)的解決方案,如果說這么說不太清楚的話,最常見的場景就是多個(gè)網(wǎng)絡(luò)請求都結(jié)束后觸發(fā)列表刷新。

其實(shí)這個(gè)需求呢,還是挺常見的。主要呢,目前有兩種解決思路,一種呢是GCD中的dispatch_group,一種是NSOperation

dispatch_group

這個(gè)方案呢,實(shí)現(xiàn)起來還比較簡單,先放一下代碼吧。

-(void)testGCDGroup {
    dispatch_group_t g = dispatch_group_create();
    dispatch_queue_t q = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"Will enter task1");
    dispatch_group_enter(g);
    dispatch_group_async(g, q, ^{
        [self task1];
        dispatch_group_leave(g);
    });
    NSLog(@"Will enter task2");
    dispatch_group_enter(g);
    dispatch_group_async(g, q, ^{
        [self task2];
        dispatch_group_leave(g);
    });
    
    NSLog(@"Come to notify");
    dispatch_group_notify(g, q, ^{
        NSLog(@"Enter notify");
        [self taskComplete];
    });
    NSLog(@"Pass notify");
}

-(void)task1 {
    NSLog(@"Enter sleep 10.");
    [NSThread sleepForTimeInterval:10];
    NSLog(@"Leave sleep 10.");
}

-(void)task2 {
    NSLog(@"Enter sleep 5.");
    [NSThread sleepForTimeInterval:5];
    NSLog(@"Leave sleep 5.");
}

-(void)taskComplete {
    NSLog(@"All task finished.");
}

控制臺(tái)輸出是這個(gè)樣子的:

2018-03-26 14:28:02.317556+0800 test[3446:287435] Will enter task1.
2018-03-26 14:28:02.317714+0800 test[3446:287435] Will enter task2.
2018-03-26 14:28:02.317733+0800 test[3446:287484] Enter sleep 10.
2018-03-26 14:28:02.317847+0800 test[3446:287435] Come to notify.
2018-03-26 14:28:02.317865+0800 test[3446:287486] Enter sleep 5.
2018-03-26 14:28:02.318093+0800 test[3446:287435] Pass notify.
2018-03-26 14:28:07.318474+0800 test[3446:287486] Leave sleep 5.
2018-03-26 14:28:12.321389+0800 test[3446:287484] Leave sleep 10.
2018-03-26 14:28:12.321740+0800 test[3446:287484] Enter notify.
2018-03-26 14:28:12.321932+0800 test[3446:287484] All task finished.

他呢,基本流程就是當(dāng)調(diào)用的dispatch_group_leave()與dispatch_group_enter()相等時(shí),就會(huì)調(diào)用dispatch_group_notify()中的回調(diào)。不過這種實(shí)現(xiàn)方案呢,還是有一個(gè)需要注意的點(diǎn)就是dispatch_group_enter()與dispatch_group_leave()要成對使用,否則就會(huì)進(jìn)入無限的等待狀態(tài)

第二個(gè)解決方案就是使用NSOperation。吶,我會(huì)放在第二節(jié)著重介紹一下的。


NSOperation子類重寫

我們知道,NSOperation是蘋果提供的一套面向?qū)ο蟮幕贕CD封裝的多線程解決方案。他在使用上更加符合面向?qū)ο蟮乃枷耄臃奖愕臑槿蝿?wù)添加依賴關(guān)系,同時(shí)提供了四個(gè)支持KVO監(jiān)聽的代表當(dāng)前任務(wù)執(zhí)行狀態(tài)的屬性cancelled、executing、finished、ready。NSOperation內(nèi)部對這四個(gè)狀態(tài)行為作了預(yù)處理,根據(jù)任務(wù)的不同狀態(tài)這四個(gè)屬性的值會(huì)自動(dòng)改變。當(dāng)NSOperation配合NSOperationQueue使用時(shí),Queue會(huì)監(jiān)聽所有Operation的狀態(tài)從而分配任務(wù)的啟動(dòng)時(shí)機(jī)。總之,NSOperation隱藏了很多內(nèi)部細(xì)節(jié),讓我們開發(fā)者無需關(guān)心任務(wù)的各種狀態(tài)。

系統(tǒng)行為

首先,為了模仿系統(tǒng)行為,我們先觀察下系統(tǒng)的NSOperation的cancelled、executing、finished、ready四個(gè)屬性的狀態(tài)變化情況。那我們?nèi)ケO(jiān)聽一下NSOperation的四個(gè)屬性。代碼如下:

TestBlockOperation * bp1 = [TestBlockOperation blockOperationWithBlock:^{
        NSLog(@"enter bp1");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"leave bp1");
}];
NSArray * keyPathes = @[@"isReady",@"isCancelled",@"isExecuting",@"isFinished"];
[self logOp:bp1 keyPathes:keyPathes];  
[self addObserverForOp:bp1 keyPathes:keyPathes];
[bp1 start];
[bp1 cancel];

控制臺(tái)輸出:
2018-04-18 11:45:01.277354+0800 OperationDemo[72212:1655503] bp1 isReady = true
2018-04-18 11:45:01.277539+0800 OperationDemo[72212:1655503] bp1 isCancelled = false
2018-04-18 11:45:01.278212+0800 OperationDemo[72212:1655503] bp1 isExecuting = false
2018-04-18 11:45:01.278449+0800 OperationDemo[72212:1655503] bp1 isFinished = false
2018-04-18 11:45:01.278682+0800 OperationDemo[72212:1655503] bp1 before start
2018-04-18 11:45:01.278954+0800 OperationDemo[72212:1655503] bp1---isExecuting---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 11:45:01.279063+0800 OperationDemo[72212:1655503] bp1 before main
2018-04-18 11:45:01.279245+0800 OperationDemo[72212:1655503] enter bp1
2018-04-18 11:45:04.279669+0800 OperationDemo[72212:1655503] leave bp1
2018-04-18 11:45:04.280074+0800 OperationDemo[72212:1655503] bp1 after main
2018-04-18 11:45:04.281164+0800 OperationDemo[72212:1655503] bp1---isExecuting---{
    kind = 1;
    new = 0;
    old = 1;
}
2018-04-18 11:45:04.281404+0800 OperationDemo[72212:1655503] bp1---isFinished---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 11:45:04.281557+0800 OperationDemo[72212:1655503] bp1 after start
2018-04-18 11:45:04.281782+0800 OperationDemo[72212:1655503] bp1 before cancel
2018-04-18 11:45:04.281917+0800 OperationDemo[72212:1655503] bp1 after cancel

上述代碼中,我們監(jiān)聽了四個(gè)屬性并執(zhí)行了Operation。根據(jù)日志我們可以總結(jié)如下:

初始狀態(tài)下,ready為YES,其他均為NO

當(dāng)我們調(diào)用 -start 后,執(zhí)行 -main 之前 isExecuting 屬性從NO被置為YES

調(diào)用 -main 之后開始執(zhí)行提交到Operation中的任務(wù)

任務(wù)完成后 isExecuting 屬性從YES被置為NO,isFinished 屬性從NO被置為YES

我們再看一下如果在執(zhí)行 -start 之前先執(zhí)行 -cancel 后會(huì)是什么狀態(tài):

TestBlockOperation * bp2 = [TestBlockOperation blockOperationWithBlock:^{
        NSLog(@"enter bp2");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"leave bp2");
}];
[self addObserverForObj:bp2 keyPathes:keyPathes];
self.bp2 = bp2;
[bp2 cancel];
[bp2 start];

控制臺(tái)輸出:
2018-04-18 11:44:03.597414+0800 OperationDemo[72184:1653790] bp2 before cancel
2018-04-18 11:44:03.597684+0800 OperationDemo[72184:1653790] bp2---isCancelled---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 11:44:03.597881+0800 OperationDemo[72184:1653790] bp2---isReady---{
    kind = 1;
    new = 1;
    old = 1;
}
2018-04-18 11:44:03.598051+0800 OperationDemo[72184:1653790] bp2 after cancel
2018-04-18 11:44:03.598138+0800 OperationDemo[72184:1653790] bp2 before start
2018-04-18 11:44:03.598279+0800 OperationDemo[72184:1653790] bp2---isFinished---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 11:44:03.598393+0800 OperationDemo[72184:1653790] bp2 after start

在執(zhí)行 -start 之前調(diào)用 -cancel 后,isCancelled 屬性從NO被置為YES,isReady 屬性無論什么狀態(tài)都會(huì)被置為YES。這里后面講到dependency的時(shí)候會(huì)說明。

-cancel 之后再調(diào)用 -start ,會(huì)將 isFinished 屬性從NO被置為YES,然后并不調(diào)用 -main 方法。

單個(gè)Operation的行為我們已經(jīng)基本了解,那么接下來我們來看一下當(dāng)兩個(gè)Operation添加到Queue中會(huì)是什么結(jié)果。

TestBlockOperation * bp1 = [TestBlockOperation blockOperationWithBlock:^{
        NSLog(@"enter bp1");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"leave bp1");
}];
bp1.name = @"bp1";
bp1.completionBlock = ^{
        NSLog(@"bp1 complete");
};
    
TestBlockOperation * bp2 = [TestBlockOperation blockOperationWithBlock:^{
        NSLog(@"enter bp2");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"leave bp2");
}];
bp2.name = @"bp2";
bp2.completionBlock = ^{
        NSLog(@"bp2 complete");
};
    
NSArray * keyPathes = @[@"isReady",@"isCancelled",@"isExecuting",@"isFinished"];
    
[self addObserverForOp:bp1 keyPathes:keyPathes];
[self addObserverForOp:bp2 keyPathes:keyPathes];
    
NSOperationQueue * q = [NSOperationQueue new];
[bp1 addDependency:bp2];
[q addOperation:bp1];
[q addOperation:bp2];
    
控制臺(tái)輸出:
2018-04-18 16:37:16.004963+0800 OperationDemo[84411:1940169] bp1 before addDependency:
2018-04-18 16:37:16.005291+0800 OperationDemo[84411:1940169] bp1---isReady---{
    kind = 1;
    new = 0;
    old = 1;
}
2018-04-18 16:37:16.005640+0800 OperationDemo[84411:1940169] bp1 after addDependency:
2018-04-18 16:37:16.005842+0800 OperationDemo[84411:1940219] bp2 before start
2018-04-18 16:37:16.006277+0800 OperationDemo[84411:1940219] bp2---isExecuting---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:37:16.007394+0800 OperationDemo[84411:1940219] bp2 before main
2018-04-18 16:37:16.007669+0800 OperationDemo[84411:1940219] enter bp2
2018-04-18 16:37:19.010134+0800 OperationDemo[84411:1940219] leave bp2
2018-04-18 16:37:19.010351+0800 OperationDemo[84411:1940219] bp2 after main
2018-04-18 16:37:19.010701+0800 OperationDemo[84411:1940218] bp1 before start
2018-04-18 16:37:19.010707+0800 OperationDemo[84411:1940219] bp1---isReady---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:37:19.010857+0800 OperationDemo[84411:1940218] bp1---isExecuting---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:37:19.011126+0800 OperationDemo[84411:1940219] bp2---isExecuting---{
    kind = 1;
    new = 0;
    old = 1;
}
2018-04-18 16:37:19.011134+0800 OperationDemo[84411:1940218] bp1 before main
2018-04-18 16:37:19.011143+0800 OperationDemo[84411:1940220] bp2 complete
2018-04-18 16:37:19.011229+0800 OperationDemo[84411:1940218] enter bp1
2018-04-18 16:37:19.011233+0800 OperationDemo[84411:1940219] bp2---isFinished---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:37:19.011458+0800 OperationDemo[84411:1940219] bp2 after start
2018-04-18 16:37:22.011382+0800 OperationDemo[84411:1940218] leave bp1
2018-04-18 16:37:22.011571+0800 OperationDemo[84411:1940218] bp1 after main
2018-04-18 16:37:22.012029+0800 OperationDemo[84411:1940218] bp1---isExecuting---{
    kind = 1;
    new = 0;
    old = 1;
}
2018-04-18 16:37:22.012050+0800 OperationDemo[84411:1940219] bp1 complete
2018-04-18 16:37:22.012375+0800 OperationDemo[84411:1940218] bp1---isFinished---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:37:22.013382+0800 OperationDemo[84411:1940218] bp1 after start

當(dāng)為bp1添加bp2作為依賴以后,bp1的 isReady 屬性從YES置為NO。

由于bp2是bp1的依賴,所以優(yōu)先執(zhí)行bp2。

在bp2中任務(wù)完成之后,-main 方法調(diào)用結(jié)束之后, -start 方法調(diào)用結(jié)束之前,bp1調(diào)用 -start 并將 isReady 屬性置為YES。

其他行為與單個(gè)調(diào)用時(shí)基本一致。

我們再來看看當(dāng)bp1添加bp2作為依賴,并且在調(diào)用之前bp2調(diào)用 -cancel 時(shí)的狀態(tài)變化,代碼基本一致,唯一變化是在添加在q之前bp2調(diào)用 -cancel,我就不放代碼了,直接看日志輸出:

2018-04-18 16:39:38.612072+0800 OperationDemo[84462:1944038] bp1 before addDependency:
2018-04-18 16:39:38.612500+0800 OperationDemo[84462:1944038] bp1---isReady---{
    kind = 1;
    new = 0;
    old = 1;
}
2018-04-18 16:39:38.612712+0800 OperationDemo[84462:1944038] bp1 after addDependency:
2018-04-18 16:39:38.613460+0800 OperationDemo[84462:1944038] bp2 before cancel
2018-04-18 16:39:38.613984+0800 OperationDemo[84462:1944038] bp2---isCancelled---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:39:38.614337+0800 OperationDemo[84462:1944038] bp2---isReady---{
    kind = 1;
    new = 1;
    old = 1;
}
2018-04-18 16:39:38.614512+0800 OperationDemo[84462:1944038] bp2 after cancel
2018-04-18 16:39:38.614804+0800 OperationDemo[84462:1944152] bp2 before start
2018-04-18 16:39:38.615286+0800 OperationDemo[84462:1944158] bp1 before start
2018-04-18 16:39:38.615321+0800 OperationDemo[84462:1944152] bp1---isReady---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:39:38.615614+0800 OperationDemo[84462:1944158] bp1---isExecuting---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:39:38.615629+0800 OperationDemo[84462:1944150] bp2 complete
2018-04-18 16:39:38.615661+0800 OperationDemo[84462:1944152] bp2---isFinished---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:39:38.616030+0800 OperationDemo[84462:1944158] bp1 before main
2018-04-18 16:39:38.616115+0800 OperationDemo[84462:1944152] bp2 after start
2018-04-18 16:39:38.616132+0800 OperationDemo[84462:1944158] enter bp1
2018-04-18 16:39:41.618815+0800 OperationDemo[84462:1944158] leave bp1
2018-04-18 16:39:41.619170+0800 OperationDemo[84462:1944158] bp1 after main
2018-04-18 16:39:41.619551+0800 OperationDemo[84462:1944152] bp1 complete
2018-04-18 16:39:41.619591+0800 OperationDemo[84462:1944158] bp1---isExecuting---{
    kind = 1;
    new = 0;
    old = 1;
}
2018-04-18 16:39:41.619941+0800 OperationDemo[84462:1944158] bp1---isFinished---{
    kind = 1;
    new = 1;
    old = 0;
}
2018-04-18 16:39:41.620073+0800 OperationDemo[84462:1944158] bp1 after start

與單個(gè)Operation調(diào)用 -cancel 行為一致,不影響 -start 的調(diào)用,同樣不會(huì)調(diào)用 -main,不同點(diǎn)是在bp1調(diào)用 -start 之前 isReady 屬性會(huì)被置為YES,之后行為與單個(gè)Operation調(diào)用 -start 一致。

上述行為可以用一張流程圖來表現(xiàn):


Operation流程

重寫子類

通過觀察上述的日志我們可以看出,當(dāng)一個(gè)任務(wù)作為另一個(gè)任務(wù)的依賴時(shí),只有當(dāng)被依賴的任務(wù)完成后,才會(huì)執(zhí)行另一個(gè)任務(wù),而這個(gè)時(shí)間點(diǎn)的時(shí)候,executing、finished兩個(gè)屬性會(huì)發(fā)生變化。那我們需要做的就是實(shí)現(xiàn)一個(gè)NSOperation的子類,讓他可以再我們需要的時(shí)候才被標(biāo)記為完成狀態(tài),這樣,我們只要給刷新列表任務(wù)添加網(wǎng)絡(luò)請求任務(wù)作為依賴即可。所以,我們需要做的只有兩件事,就是接過executing、finished兩個(gè)屬性的管理權(quán)以及在我們需要的時(shí)候改變他們的狀態(tài)。

需求知道了,實(shí)現(xiàn)就很簡單了。老司機(jī)直接就放一份簡單的源碼就好了。

@class DWManualOperation;
typedef void(^OperationHandler)(DWManualOperation * op);
@interface DWManualOperation : NSOperation

/**
 以需要實(shí)現(xiàn)的任務(wù)生成operation對象

 @param handler 需要實(shí)現(xiàn)的任務(wù)
 @return operation實(shí)例
 
 */
+(instancetype)manualOperationWithHandler:(OperationHandler)handler;

/**
 立刻將當(dāng)前任務(wù)標(biāo)識為完成狀態(tài),isExecuting 為 NO,isFinished 為 YES。
 */
-(void)finishOperation;

@end

@interface DWManualOperation ()

@property (nonatomic ,assign ,getter=isFinished) BOOL finished;

@property (nonatomic ,assign ,getter=isExecuting) BOOL executing;

@property (nonatomic ,copy) OperationHandler handler;

@property (nonatomic ,strong) DWManualOperation * cycleSelf;

@end

@implementation DWManualOperation
@synthesize finished = _finished;
@synthesize executing = _executing;

#pragma mark --- interface method ---
+(instancetype)manualOperationWithHandler:(OperationHandler)handler {
    DWManualOperation * op = [DWManualOperation new];
    if (handler) {
        op.handler = handler;
    }
    return op;
}

-(void)finishOperation {
    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];
    _finished = YES;
    _executing = NO;
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

#pragma mark --- override ---
-(instancetype)init {
    if (self = [super init]) {
        _concurrentHandler = YES;
        self.completionBlock = nil;
    }
    return self;
}

-(void)start {
    NSLog(@"start");
    ///如果是被取消狀態(tài)則置為完成狀態(tài)并返回,為了配合NSOperationQueue使用
    if (self.isCancelled) {
        [self willChangeValueForKey:@"isFinished"];
        _finished = YES;
        [self didChangeValueForKey:@"isFinished"];
        return;
    }
    if (self.isExecuting || self.isFinished) {///正在執(zhí)行或已經(jīng)完成的任務(wù)不可以調(diào)用開始方法。
        return;
    }
    self.cycleSelf = self;
    [super start];
}

-(void)cancel {
    [super cancel];
}

-(void)main {///系統(tǒng)實(shí)現(xiàn)中 -start 方法中會(huì)調(diào)用 -main 方法
    [self willChangeValueForKey:@"isExecuting"];
    _executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
    [super main];
    __weak typeof(self)weakSelf = self;
    if (self.handler) {
        self.handler(weakSelf);
    }
}

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

#pragma mark --- tool func ---
static inline void freeOperation(DWManualOperation * op) {
    op.cycleSelf = nil;
}

#pragma mark --- setter/getter ---

-(void)setCompletionBlock:(void (^)(void))completionBlock {
    __weak typeof(self)weakSelf = self;
    dispatch_block_t ab = ^(void) {
        if (completionBlock) {
            completionBlock();
        }
        freeOperation(weakSelf);
    };
    [super setCompletionBlock:ab];
}

@end

吶,寫到這里,我們就基本實(shí)現(xiàn)了一個(gè)跟系統(tǒng)Operation具有相同行為,但是我們可以隨意控制Operation是否完成的子類了。


條件模塊

不知道該叫什么我就隨便起了個(gè)名,其實(shí)就是一個(gè)應(yīng)用,場景就是操作A一定要建立在某種狀態(tài)下才能執(zhí)行。最簡單的就是比如點(diǎn)贊功能必須是登錄后才可進(jìn)行,那么我們就要對這種狀態(tài)做出判斷。如下圖:

條件模塊

你可能說這無非就是一個(gè)判斷的事,的確是,不過像登錄狀態(tài)這種很多地方都要用的功能這樣寫也能很好的復(fù)用。這個(gè)思路能主要還是借鑒的大神Delpan的這篇博客:《操作抽象設(shè)計(jì)-實(shí)踐》,寫的很好,同學(xué)們感興趣可以去看看。

Demo傳送門


請求類封裝

吶,寫到這里其實(shí)就只是講思路了,至此我們已經(jīng)具有了一個(gè)可以控制完成時(shí)機(jī)的Operation了,只要我們將網(wǎng)絡(luò)請求與Operation同時(shí) -start 后,請求回調(diào)結(jié)束后標(biāo)志Operation為完成狀態(tài)后就可以為請求添加依賴了,同時(shí)也可以配合系統(tǒng)的其他Operation和Queue同時(shí)使用完成線程間通信。

說到這要是就結(jié)束了那就太虎頭蛇尾了,而且真愛粉們應(yīng)該知道,一般到這個(gè)時(shí)候就是老司機(jī)的軟廣環(huán)節(jié)了,著急的童鞋們可以關(guān)掉瀏覽器了哈~

2.jpg

老司機(jī)給予這個(gè)思路對AFN進(jìn)行了二次封裝,寫了一個(gè)自用的請求框架DWFlashFlow

首先它具有NSOperation的所有特性,可以跟普通Operation結(jié)合在一起使用,其次我還封裝了批量請求和請求量功能,并且在功能層和邏輯層上進(jìn)行了分離,也就是說你可以自由更換你的請求核心類,而邏輯層不變~哎,最近都不會(huì)吹牛逼了,剩下的東西喜歡的同學(xué)自己看吧~

傳送門:DWFlashFlow

喜歡的童靴給點(diǎn)個(gè)小星星唄~

最后編輯于
©著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
12人點(diǎn)贊
老司機(jī)Wicky專注逗逼20年的老司機(jī)開始摳代碼了
總資產(chǎn)721共写了8.1W字获得2,296个赞共2,856个粉丝
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,025評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,016評論 2 375

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