📚Effective OC - Tip 43 , 44 , 45 , 46 GCD

掌握 GCD 和操作队列的使用时机

//自定义NSOperation
@interface CustomNSOperation()
//需要设置, 不然不能释放Operation,cancel 里面调用 setFinished , 发出KVO 才会释放
@property (assign, nonatomic, getter = isExecuting) BOOL executing;
@property (assign, nonatomic, getter = isFinished) BOOL finished;
@end

@implementation CustomNSOperation {

}

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

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

- (void)cancel {
    NSLog(@"cancel CustomNSOperation");
    [super cancel];
    self.finished = YES;
}

- (void)setExecuting:(BOOL)executing {
    [self willChangeValueForKey:@"isExecuting"];
    _executing = executing;
    [self didChangeValueForKey:@"isExecuting"];
}

- (void)setFinished:(BOOL)finished {
    [self willChangeValueForKey:@"isFinished"];
    _finished = finished;
    [self didChangeValueForKey:@"isFinished"];
}

- (void)main {
    NSLog(@"do CustomNSOperation");
}
@end


/**
 * NSOperationQueue 和 NSOperation 的优点
 * 1. 可以cancel
 * 2. 可以添加dependencies
 * 3. 可以 KVO
 * 4. 可以指定操作的优先级
 */
//获取当前线程的方法用[NSThread currentThread] , 而不是 dispatch_get_current_queue ,
// NSBlockOperation 直接start ,会并发执行所有block,也会再当前的队列上执行
// 而被添加到 operationQueue 则按照 Queue 的模式来并发执行
- (void)testNSOperation {
    //NSBlockOperation
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        sleep(3);
        NSLog(@"blockOperation 1 %@", [NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        sleep(1);
        NSLog(@"blockOperation 2 %@", [NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        sleep(2);
        NSLog(@"blockOperation 3 %@", [NSThread currentThread]);
    }];
    [operation setCompletionBlock:^{
        NSLog(@"blockOperation 4 %@", [NSThread currentThread]);
    }];
    [operation setCompletionBlock:^{
        NSLog(@"completed blockOperation %@", [NSThread currentThread]);
    }];
//    [operation start];

    //NSInvocationOperation
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                     selector:@selector(invocationMessage)
                                                                                        object:nil];
//
    [invocationOperation setQualityOfService:NSQualityOfServiceUserInteractive];
    [invocationOperation setQualityOfService:NSQualityOfServiceUserInitiated];
    [invocationOperation setQualityOfService:NSQualityOfServiceUtility];
    [invocationOperation setQualityOfService:NSQualityOfServiceBackground];
    [invocationOperation setQualityOfService:NSQualityOfServiceDefault];
    [invocationOperation setQualityOfService:NSQualityOfServiceBackground];
    [_operationQueue addOperation:invocationOperation];

    //CustomNSOperation
    CustomNSOperation *customNSOperation = [CustomNSOperation new];
    [_operationQueue addOperation:customNSOperation];
    NSLog(@"------ end %@", [NSThread currentThread]);

    _operationQueue.maxConcurrentOperationCount = 2;
    [_operationQueue addOperation:operation];
//    [_operationQueue waitUntilAllOperationsAreFinished]; //会阻塞知道任务完成
    [_operationQueue cancelAllOperations];
}

- (void)invocationMessage {
    NSLog(@"enter invocationOperation");
}

通过 Dispatch Group 机制,根据系统资源状况来执行任务

/*
 *  1. dispatch_notify 方案
 *  dispatch_group_async(group, dispatch_get_main_queue()
 *  dispatch_notify(group, dispatch_get_main_queue(),
 *  2. dispatch_group_wait 阻塞
 *  dispatch_group_enter(group);
 *  dispatch_group_leave(group);
 *  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
 *  dispatch_group_async  相当于 dispatch_group_enter  和   dispatch_group_leave
 *  3. dispatch_apply 多次
 *
 */
- (void)caseOne {
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_main_queue(), ^{
        dispatch_group_leave(group);
        NSLog(@"Task 1");
    });
    dispatch_group_async(group, dispatch_get_main_queue(), ^{
        NSLog(@"Task 1");
    });
    dispatch_group_async(group, dispatch_get_main_queue(), ^{
        NSLog(@"Task 1");
    });
    dispatch_group_async(group, dispatch_get_main_queue(), ^{
        NSLog(@"Task 1");
    });
    dispatch_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"Task finished");
    });
    NSLog(@"我直接过来了");
}

- (void)caseTwo {
    dispatch_queue_t queue = dispatch_queue_create("hello", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        dispatch_group_leave(group);
        NSLog(@"Task 1");
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        dispatch_group_leave(group);
        NSLog(@"Task 2");
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        sleep(10);
        dispatch_group_leave(group);
        NSLog(@"Task 3");
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"过来了");
}

- (void)caseThree {
    dispatch_queue_t queue = dispatch_queue_create("hello", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(10, queue, ^(size_t i) {
        NSLog(@"== output is %@", @(i));
    });
}

使用 dispatch_once 来执行只需运行一次的线程安全代码

/*
 *  dispatch_once_t
 *   整个情况下只运行一次的情况,特别适合单例,而且单例对象指针已经全局分配
 */
- (void)caseOnce {
    static dispatch_once_t once;
    static GCD* singleInstance = nil;
    dispatch_once(&once, ^{
        singleInstance = [GCD new];
    });
}

不要使用 dispatch_get_current_queue

/* 1. dispatch_get_current_queue 已被废弃,因为
 *           Global_Queue(并发)
 *                |
 *           ------------
 *           |          |
 *         QueueC      QueueD
 *        -------
 *       |     |
 *    QueueA  QueueB
 *   有这种关系存储,所以很难使用 dispatch_get_current_queue 准确的获取到当前 block_t 的 queue
 *
 * 2. 队列特定值,这个值只与队列绑定,如果属于上面的继承树,则会一直向上追溯,直到找到数据为止。
 *    dispatch_queue_set_specific(queueA, &kQueueSpecific, queueSpecificValue, CFRelease);
        dispatch_get_specific(&kQueueSpecific)  //获取当前队列的特定值
       //以下是一种避免死锁的操作
 */
- (void)caseCurrentThread {
    NSLog(@"%@", dispatch_get_current_queue());
    dispatch_queue_t queueA = dispatch_queue_create("queueA", NULL);
    static int kQueueSpecific;
    CFStringRef queueSpecificValue = CFSTR("queueA");
    dispatch_queue_set_specific(queueA, &kQueueSpecific, queueSpecificValue, CFRelease);
    dispatch_sync(queueA, ^{
        dispatch_block_t block = ^{
            NSLog(@"No dead lock");
        };
        CFStringRef value = dispatch_get_specific(&kQueueSpecific);
        if (value) {
            block();
        } else {
            dispatch_sync(queueA, block);
        }
    });

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,640评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,254评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,011评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,755评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,774评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,610评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,352评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,257评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,717评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,894评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,021评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,735评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,354评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,936评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,054评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,224评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,974评论 2 355

推荐阅读更多精彩内容