GCD

基础知识,block是很多高级特性的基础,包括GCD,ReactiveCocoa。

GCD操作方式有两种: 
同步dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>), 
异步dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>).

同步则是等block代码执行完毕才回跳转到方法下一步,异步则是直接顺序执行完当前方法,线程处理block。

串行和并行队列介绍: 

  1. serial queues(串行队列)又称私有调度队列(private),一般用再对特定资源的同步访问上。我们可以根据需要创建任意数量的串行队列,每一个串行队列之间是并发的。 
  2. 并行队列,又称global dispatch queue。并行队列虽然可以并发的执行多个任务,但是任务开始执行的顺序和其加入队列的顺序相同。我们自己不能去创建并行调度队列。只有三个可用的global concurrent queues。

  3. main dispatch queue 是一个全局可用的串行队列,其在行用程序的主线程上执行任务。此队列的任务和应用程序的主循环(run loop)要执行的事件源交替执行。因为其运行在应用程序的主线程,main queue经常用来作为应用程序的一个同步点。

放上一段经典代码:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    // 处理耗时操作的代码块...

    //通知主线程刷新
    dispatch_async(dispatch_get_main_queue(), ^{
        //回调或者说是通知主线程刷新,
    });

});

这是GCD最常用的一种手法,先开启一个异步操作,指定queue,block实现模块。 

主队列dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

全局dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

串行dispatch_queue_t queue = dispatch_queue_create("navy", NULL);

并行dispatch_queue_t queue = dispatch_queue_create("navy", DISPATCH_QUEUE_CONCURRENT);

queue有三类:

  • Main Dispatch Queue: 主线程队列(Serial Queue), 在程序的RunLoop中执行。因为main queue是与主线程相关的,所以这是一个串行队列。

  • Global Dispatch Queue: 系统中所有应用程序共用的全局队列(Concurrent Queue), 全局队列是并发队列,一般不必创建新的Dispatch Queue,使用此Queue就可以了。Global Diapacth Queue有4个优先级:High Priority(高)、Default Priority(默认)、Low Priority(低)、Background Priority(后台) 

  • 用户队列:是用函数 dispatch_queue_create创建的队列. 这些队列可以是串行或者并行的,null默认为串行。

处理完费时的工作需要刷新UI就得dispatch_get_main_queue()在主线程里刷新界面。 

dispatch_semaphore_t 
信号量的作用,就是下一步代码的执行要等待上一步的计算结果。有些队列既希望能异步执行,又希望能获取到block的结果再执行下一步操作。 

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    sleep(5);
    WWLog(@"block");
    dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
WWLog(@"finish");

这段代码先打印block,再打印finish。信号量要等待block执行结束。

这些基础清楚后,很容易写一个异步模块,抛弃NSThread,NSLock这类东西。

dispatch_once 
字面很容易理解,只执行一次,常用于单例模式的初始化,如下:

+ (instancetype)sharedInstance
{
    static RFTagDao *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[RFTagDao alloc] init];
    });
    return instance;
}    

dispatch_after() 
延迟加载 

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    NSLog(@"Waitted at least 2 seconds");
});

dispatch group 
可以等待这个组里面的queue执行完再进行下一步。 

dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_dispatchGroup = dispatch_group_create();
for (id obj in arr1) {
    dispatch_group_async(dispatchGroup, queue1, ^{
        [obj performTask];
    });
}
for (id obj in arr2) {
    dispatch_group_async(dispatchGroup, queue2, ^{
        [obj performTask];
    });
}

//如果需要阻塞线程可以使用wait
dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);
//to do something.

//不阻塞使用notify
dispatch_queue_t notifyQueue = dispatch_get_main_queue();
dispatch_group_notify(dispatchGroup,notifyQueue,^{
    //to do something.
});

dispatch_apply 
遍历执行block 

group中的for循环遍历执行async block就可以修改为使用apply方式,如下:
dispatch_apply(arr1.count, queue1, ^(size_t){
        id obj = arr[i];
        [obj performTask];
    });

不过dispatch_apply会持续阻塞,直到所有任务都执行完毕为止,如果把block指派给了current queue或者高于当前队列的某个串行队列,就会导致死锁。如果想后台执行,还是使用group安心的使用异步遍历吧。

dispatch_set_target_queue 
dispatch_set_target_queue(<#dispatch_object_t object#>, <#dispatch_queue_t queue#>)第一个参数是目标queue1,第二个是需要设置的queue2。 将queue1的的优先级设置为queue2的优先级。 
这个方法不管queue1是串行还是并行队列。 

dispatch_queue_set_specific 
最后介绍dispatch_queue_set_specific这个东西,类似objc_setAssociatedObject objc_getAssociatedObject这套玩意,在fmdb源码中就用了这个,他所有的操作都是在一个队列里进行的。 

static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey;

_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);

dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL);

FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);

assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");

这么做的主要目的就是避免死锁,设置源码如下: 

dispatch_queue_set_specific(dispatch_queue_t queue, const void *key,
void *context, dispatch_function_t destructor);
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

specific设置key value都是用的void指针,也就意味着能够存储任意数据,甚至当前对象,好处是gcd属于底层api,和corefoundation无缝桥接。 

(lldb) thread list 命令将会在控制台打印出所有队列的名字

dispatch_benchmark 
用来测试性能,能测试代码运行了多少ns,不过这是私有api,不能用在发布版本。

size_t const objectCount = 1000;
uint64_t n = dispatch_benchmark(10000, ^{
@autoreleasepool {
    id obj = @42;
    NSMutableArray *array = [NSMutableArray array];
    for (size_t i = 0; i < objectCount; ++i) {
        [array addObject:obj];
    }
}
});
NSLog(@"-[NSMutableArray addObject:] : %llu ns", n);

介绍这些应该就足够使用了,还有很多文件相关的GCD知识,只是我并不常用,就不介绍了。这有一个详细的参考链接。 iOS相关技术真正发生改变还是在于语言的改进上,在没有block之前,大部分都是delegate调用,或者脏的不行的KVO上。

有了block,代码能够更清晰直观,一个回调就不用写长长的delegate;因为block,然后CGD的出现,让线程队列也变的更容易直观;因为GCD,然后ReactiveCocoa能写更容易的响应式代码,整个的链式语法,核心signal,统一了消息传递。

动态语言早有的特性,iOS其实出的还是慢了点,现在swift也是汲取了动态语言的很多特性,可惜我还没怎么学习,不过毕竟用过python,js,学起来应该还是很容易。

不过最后还是吐槽下,并不是争语言的好坏,而是我觉得太久用动态语言或者各种语法糖结构,其实很容易养成不好的习惯,很容易让代码变得不可读,如果能做到代码即注释,便是极好的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值