GCD

GCD 调度队列是一个类似对象的结构,用于管理您提交给它的任务。所有调度队列都是先进先出数据结构。因此,添加到队列中的任务始终以与添加任务相同的顺序启动。

在当前的OS X和iOS中,ARC现在将Dispatch对象视为Obj-C对象。它们将以与Obj-C对象相同的方式进行内存管理,因此应将其strong用于属性。
这由中OS_OBJECT_USE_OBJC定义的宏控制<os/object.h>。1当您的部署目标是OS X 10.8或更高版本或iOS 6.0或更高版本时,默认情况下将其设置为该值。

@property (strong, nonatomic) dispatch_queue_t queue;
内存的释放

调度队列对自动释放的对象提供的支持最少。系统API可能会将自动释放的对象返回到您的代码中。例如,NSError对象通常是自动释放的。如果由于块中创建了自动释放的对象而导致内存压力增加,请考虑将自动释放池添加到这些块中以减轻压力。您还可以在创建时使用该功能配置任何自定义调度队列的默认自动释放行为。 dispatch_queue_attr_make_with_autorelease_frequency

dispatch queue

四种类型 dispatch_queue_t 定义
  1. dispatch_queue_serial_t serial 串行队列。一次只能执行一个任务(完成才能执行下一个),分派队列,按先进先出(FIFO)顺序连续执行任务。
  2. dispatch_queue_concurrent_t concurrent 并行队列。可以同事执行多个任务
  3. dispatch_queue_main_t main 主队列。全局可用的一个串行队列。这个队列和主线程的运行循环一起工作。
  4. dispatch_queue_global_t global queue 具有指定服务质量类的系统定义的全局并发队列

由此底层是只有两种形式的队列,并发队列和串行队列

创建获取队列

  1. dispatch_get_main_queue() 返回与应用程序主线程关联的串行调度队列。
  2. dispatch_get_global_queue 返回具有指定服务质量类的系统定义的全局并发队列。
  3. dispatch_queue_create 创建一个新的调度队列,您可以向其提交块。
  4. dispatch_queue_create_with_target 创建一个新的调度队列,您可以向其提交块。
执行
异步执行
  • dispatch_async 提交一个块以在调度队列上异步执行并立即返回。
  • dispatch_async_f 提交应用程序定义的函数以在调度队列上异步执行并立即返回。接收一个 dispatch_function_t 类型的参数作为函数,以及一个 context 数据
// dispatch_function_t 是一个 block 代码块的定义
typedef void (*dispatch_function_t)(void *_Nullable);
// context 是一个数据的指针地址,作为后面 word 代码块的参数 
void dispatch_async_f(dispatch_queue_t queue,
        void *_Nullable context, dispatch_function_t work);
// 事例
void wrapper(void* a) {
    NSLog(@"as,%@",a);
};
NSObject *vvc = objc;
dispatch_async_f(queue, (__bridge void * _Nullable)(vvc) ,&wrapper);

  • dispatch_after 使块在指定的时间执行。
  • dispatch_after_f 使应用程序定义的函数入队以在指定时间执行。
同步执行
  • dispatch_sync
  • dispatch_sync_f
  • dispatch_apply 同步的方式将代码块加入的 队列中执行指定次数。只有当传入的代码块执行完成之后,才能继续向下执行。 void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
  • dispatch_apply_f

DISPATCH_APPLY_AUTO

获取和设置上下文数据
  • dispatch_get_specific 返回与当前调度队列关联的键的值。
  • dispatch_queue_set_specific 设置指定调度队列的键/值数据。
  • dispatch_queue_get_specific 获取与指定的调度队列关联的键的值。
    void wrapper(void* a) {
        NSLog(@"as,%@",a);
    };
    
    const char * lable = "asdasdd";
    dispatch_queue_t queue = dispatch_queue_create(lable, DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_set_specific(queue, lable, CFBridgingRetain(self), &wrapper);
    dispatch_apply(20, DISPATCH_APPLY_AUTO, ^(size_t t) {
        void * x = dispatch_queue_get_specific(queue, lable);
        NSLog(@"%@",x);
    });

Dispatch Work Item

可以用来添加依赖项 或者监控句柄(之前或者执行完成之后)

创建

  • dispatch_block_create 创建一个调度块. dispatch_block_flags_t block的标志。比如优先级,上下文等
  • dispatch_block_create_with_qos_class
dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block);

dispatch_block_t dispatch_block_create_with_qos_class(dispatch_block_flags_t flags,
        dispatch_qos_class_t qos_class, int relative_priority,
        dispatch_block_t block);
 
DISPATCH_OPTIONS(dispatch_block_flags, unsigned long,
    // 指示调度块对象应充当屏障块的标志。要在 dispatch_barrier_async 中使用,直接使用无效
    DISPATCH_BLOCK_BARRIER 
    // 
    DISPATCH_BLOCK_DETACHED
            DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x2,
    DISPATCH_BLOCK_ASSIGN_CURRENT
            DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x4,
    DISPATCH_BLOCK_NO_QOS_CLASS
            DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x8,
    DISPATCH_BLOCK_INHERIT_QOS_CLASS
            DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x10,
    DISPATCH_BLOCK_ENFORCE_QOS_CLASS
            DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x20,
);
dispatch_block_wait

等待一个 dispatch_block_t 的执行完成。如果对应的block 没有加入到对立中执行,就会等待超时时间, 返回大于0,如果已经完成,就直接返回0, 会阻塞当前队列。相当于 dispatch_sync串行。可以用于代码块的以来关系

    dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(concurrentQuene, ^{
        dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT);

        dispatch_block_t block = dispatch_block_create(0, ^{
            NSLog(@"开始执行");
            [NSThread sleepForTimeInterval:2];
            NSLog(@"结束执行");
        });
        
        dispatch_async(allTasksQueue, block);
        dispatch_sync(allTasksQueue, ^{
            sleep(3);
        });
        dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
        long resutl = dispatch_block_wait(block, timeout);

        // 等待时长,10s 之后超时
       
        
        if (resutl == 0) {
            NSLog(@"执行成功");
        } else {
            NSLog(@"执行超时");
        }
    });
dispatch_block_perform

以特定标志运行block。等同于代码

dispatch_block_t b = dispatch_block_create(flags, block);
b();
Block_release(b);
dispatch_block_notify

当置顶的代码块完成之后,执行监听的代码块。 注意第一个要监听的代码块需要已经加入到对队列中,否则会直接执行监听的程序。可以用于依赖的实现

    dispatch_queue_t queue = dispatch_queue_create("asddd", DISPATCH_QUEUE_CONCURRENT);

    dispatch_block_t t = dispatch_block_create(0, ^{
        NSLog(@"1");
        sleep(2);
        NSLog(@"3");
    });
    dispatch_async(queue, t);
    dispatch_block_notify(t, dispatch_queue_create("asdd", DISPATCH_QUEUE_CONCURRENT), ^{
        NSLog(@"完成");
    });
dispatch_block_cancel 异步取消指定的调度块。
dispatch_block_testcancel 测试给定的调度块是否已取消。

Dispatch Group

可以将不同队列中的多个块附加到一个组上,用于等待处理所有块执行完成后的处理程序。并且 只会监听 dispatch_group_async 加入的block。不会观察queue中其他的block对象

    dispatch_queue_t queue = dispatch_queue_create("asddd", DISPATCH_QUEUE_CONCURRENT);

    dispatch_block_t t = dispatch_block_create(0, ^{
        printf("\n queue block");
        sleep(3);
        printf("\n queue block finish");
    });
    dispatch_async(queue, t);
    dispatch_block_notify(t, dispatch_queue_create("asdd", DISPATCH_QUEUE_CONCURRENT), ^{
        printf("\n queue block finish notifiy");
    });
    

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        printf("\n group block");
        sleep(1);
        printf("\n group block finish");
    });
    dispatch_group_notify(group, queue, ^{
        printf("\n group block finish notify");
    });
    
 queue block
 group block
 group block finish
 group block finish notify
 queue block finish
 queue block finish notifiy
    

类似于 workitem 也有相似的 wait, 和notify 程序用户管理组的处理

    // 对于queue 并不是执行的监控queue, 而是监控完成时,指定监控代码块再 queue 的队列中执行
    dispatch_group_notify(group, queue, ^{
        NSLog(@"完成1");
    });
  • dispatch_group_notify 计划一组先前提交的块对象完成后将要提交给队列的块对象。
  • dispatch_group_notify_f 调度一组先前提交的块对象完成后,将应用程序定义的函数提交给队列。
  • dispatch_group_wait 同步等待先前提交的块对象完成;如果块在指定的超时时间之前未完成,则返回。
dispatch_group_enter / dispatch_group_leave

对于不是通过 dispatch_group_async 加入的代码块,或者其他方法,也想通过 group 进行管理监听 任务组完成,可以使用 dispatch_group_enter 和 dispatch_group_leave。

dispatch_group_enter 和 dispatch_group_leave 要成对出现,如果 dispatch_group_enter 比较多,则会任务组不会触发 dispatch_group_notify ,wait 会超时。如果 leave 多,会崩溃。 一个类似于上面的例子。

    dispatch_queue_t queue = dispatch_queue_create("asddd", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_block_t t = dispatch_block_create(0, ^{
        dispatch_group_enter(group);
        printf("\n queue block");
        sleep(3);
        printf("\n queue block finish");
        dispatch_group_leave(group);
    });
    dispatch_async(queue, t);
    dispatch_block_notify(t, dispatch_queue_create("asdd", DISPATCH_QUEUE_CONCURRENT), ^{
        printf("\n queue block finish notifiy");
    });
 // 此时的 group notify 会在 queue的block leave() 执行之后才会 执行 
 queue block
 group block
 group block finish
 queue block finish
 group block finish notify
 queue block finish notifiy
 
// 如果 leave 再等待3s 之前执行
    dispatch_queue_t queue = dispatch_queue_create("asddd", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_block_t t = dispatch_block_create(0, ^{
        dispatch_group_enter(group);
        printf("\n queue block");
        dispatch_group_leave(group);
        sleep(3);
        printf("\n queue block finish");
        
    });
    dispatch_async(queue, t);
    dispatch_block_notify(t, dispatch_queue_create("asdd", DISPATCH_QUEUE_CONCURRENT), ^{
        printf("\n queue block finish notifiy");
    });
    
 queue block
 group block
 group block finish
 group block finish notify
 queue block finish
 queue block finish notifiy

系统事件监控

dispatch source

创建一个新的调度源以监视低级系统事件。

创建 dispatch_source_create
// dispatch_source_type_t 调度源正在监视的系统对象类型的标识符
// handle 要监视的基础系统句柄。此参数的解释由type参数中提供的常量确定。可以传0 
// mask 指定所需事件的标志掩码。此参数的解释由type参数中提供的常量确定。
// queue 时间处理的调度队列
dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, uintptr_t mask, dispatch_queue_t queue);
  • type dispatch源可处理的事件
  • handle 可以理解为句柄、索引或id,假如要监听进程,需要传入进程的ID
  • mask 可以理解为描述,提供更详细的描述,让它知道具体要监听什么
  • queue 自定义源需要的一个队列,用来处理所有的响应句柄(block)
dispatch_source_type_t Dispatch Source可处理的事件
  • DISPATCH_SOURCE_TYPE_DATA_ADD 自定义的事件,变量增加
  • DISPATCH_SOURCE_TYPE_DATA_OR 自定义的事件,变量OR
  • DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口发送
  • DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
  • DISPATCH_SOURCE_TYPE_PROC 进程监听,如进程的退出、创建一个或更多的子线程、进程收到UNIX信号
  • DISPATCH_SOURCE_TYPE_READ IO操作,如对文件的操作、socket操作的读响应
  • DISPATCH_SOURCE_TYPE_SIGNAL 接收到UNIX信号时响应
  • DISPATCH_SOURCE_TYPE_TIMER 定时器
  • DISPATCH_SOURCE_TYPE_VNODE 文件状态监听,文件被删除、移动、重命名
  • DISPATCH_SOURCE_TYPE_WRITE IO操作,如对文件的操作、socket操作的写响应

DISPATCH_SOURCE_TYPE_DATA_ADD
当同一时间,一个事件的的触发频率很高,那么Dispatch Source会将这些响应以ADD的方式进行累积,然后等系统空闲时最终处理,如果触发频率比较零散,那么Dispatch Source会将这些事件分别响应。

DISPATCH_SOURCE_TYPE_DATA_OR 和上面的一样,是自定义的事件,但是它是以OR的方式进行累积

设置source 相关heander
  • dispatch_source_set_registration_handler_f 设置给定调度源的注册处理函数。
  • dispatch_source_set_registration_handler 设置给定调度源的注册处理程序块。
  • dispatch_source_set_event_handler_f 设置给定调度源的事件处理函数。
  • dispatch_source_set_event_handler 为给定的调度源设置事件处理程序块。
  • dispatch_source_set_cancel_handler_f 设置给定调度源的取消处理函数。
  • dispatch_source_set_cancel_handler 设置给定调度源的取消处理程序块。
dispatch_source_merge_data 和 dispatch_source_get_data 对应会发生

对于 DISPATCH_SOURCE_TYPE_DATA_ADD,DISPATCH_SOURCE_TYPE_DATA_OR, DISPATCH_SOURCE_TYPE_DATA_REPLACE 这三种处理时间,可以使用 dispatch_source_merge_data 进行触发。 然后在 dispatch_source_set_event_handler 设置的方法中根据 dispatch_source_get_data 获取处理过后的数据

  1. add 会对触发的数据相加传入
  2. or 试对触发的数据进行 or 运算
  3. replace 则试后面的替换掉前面的 data
    dispatch_queue_t queue = dispatch_queue_create("sss", DISPATCH_QUEUE_CONCURRENT);
    // 防止source 销毁
    self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_REPLACE, 0, 0, queue);
    
    dispatch_source_set_event_handler(self.source, ^{
        intptr_t d = dispatch_source_get_data(wself.source);
        printf("\n-- %zu   %d \n", d);
    });
    
    dispatch_async(queue, ^{
        for(int i =0 ;i < 100; i++){
            printf("%d",i);
            dispatch_source_merge_data(wself.source, i);
            sleep(0.1);
        }
    });
    // 启动source
    dispatch_resume(self.source);
    
DISPATCH_SOURCE_TYPE_TIMER 设置timer 循环
    dispatch_queue_t queue = dispatch_queue_create("sss", DISPATCH_QUEUE_CONCURRENT);
    self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    dispatch_source_set_timer(self.source, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
    
    __weak ViewController * wself = self;
    self.a = 10;
    
    dispatch_source_set_event_handler(self.source, ^{
        uintptr_t data = dispatch_source_get_data(wself.source);
        printf("\n dispatch_source_set_event_handler %d  %zu ", wself.a, data);
        wself.a = wself.a - 1;
        if(wself.a <= 0){
            dispatch_source_cancel(wself.source);
        }
    });
    
    dispatch_source_set_cancel_handler(self.source, ^{
        printf("\n dispatch_source_set_cancel_handler");
    });
    
    dispatch_source_set_cancel_handler(self.source, ^{
        printf("\n dispatch_source_set_cancel_handler");
    });
    
    dispatch_resume(self.source);
取消
  • dispatch_source_cancel 异步取消调度源,以防止进一步调用其事件处理程序块。
  • dispatch_source_testcancel 测试给定的调度源是否已取消。

Dispatch Data

DispatchSemaphore 信号量

  1. 创建 dispatch_semaphore_create(int) 传入一个大于0 的整数
  2. 去判断等待 dispatch_semaphore_wait
  3. 释放信号量 dispatch_semaphore_signal
面试题
  1. 异步的队列 可能打印的结果。根据能确定的 同步执行,确定可能的先后顺序
    dispatch_queue_t queue = dispatch_queue_create("concurrent",     DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    NSLog(@"0");
    dispatch_async(queue, ^{
        NSLog(@"3");
    });
    dispatch_async(queue, ^{
        NSLog(@"4");
    });
    // 唯一可以确定的是 0 一定再 3 和 4 之前
耗能

只要调用 同步调度异步调度 都会耗时。 计算 dispatch_async的执行时间 。

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