Dispatch Source
GCD 中除了主要的 Disaptch Queue 外,还有不太引人注目的 Dispatch Source。它是 BSD 系统内核功能 kqueue 的包装,其 CPU 内核非常小,尽量不占用资源。
Dispatch Source 可处理以下事件:
名称 | 内容 |
---|---|
DISPATCH_SOURCE_TYPE_DATA_ADD | 变量增加 |
DISPATCH_SOURCE_TYPE_DATA_OR | 变量 OR |
DISPATCH_SOURCE_TYPE_DATA_REPLACE | 变量替换 |
DISPATCH_SOURCE_TYPE_MACH_SEND | MACH 端口发送 |
DISPATCH_SOURCE_TYPE_MACH_RECV | MACH 端口接收 |
DISPATCH_SOURCE_TYPE_PROC | 检测到与进程相关的事件 |
DISPATCH_SOURCE_TYPE_READ | 可读取文件映像 |
DISPATCH_SOURCE_TYPE_SIGNAL | 接收信号 |
DISPATCH_SOURCE_TYPE_TIMER | 定时器 |
DISPATCH_SOURCE_TYPE_VNODE | 文件系统有变更 |
DISPATCH_SOURCE_TYPE_WRITE | 可写入文件影响 |
DISPATCH_SOURCE_TYPE_TIMER
定时器的例子: 10 秒后执行,执行完后取消,允许延迟 1s,代码如下;
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 10 *NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 1 *NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"10s 后");
dispatch_source_cancel(timer);
});
dispatch_source_set_cancel_handler(timer, ^{
NSLog(@"canceled");
});
dispatch_resume(timer);
dispatch_after
经常会有这样的情况,想在 x 秒后执行处理。在这种想在指定时间后执行处理的情况,可使用 dispatch_after
函数来实现。
在 3 秒后将指定的 Block
追加到 Main Dispatch Queue 中的源代码如下:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(),^{
NSLog(^"waited at least three seconds.M);
});
需要注意的是,
dispatch_after
函数并不是在指定时间后执行姓理,而只是在指定时间追加处理到 Dispatch Queue 。此源代码与在 3 秒后用dispatch async
函数追加Block
到 Main Dispatch Queue 的相同。
dispatch_after
第一个参数是指定时间用的 dispatch_time_t
类型的值。该值使用 dispatch_time
函数或 dispatch_walltime
函数生成。dispatch_time
函数能够获取从第一个参数 dispatch_time_t
类型值中指定的时间开始,到第二个参数指定的毫微秒单位时间后的时间。第一个参数经常使用的值是之前源代码中出现的 DISPATCH_TIME_NOW
这表示现在的时间。即以下源代码可得到表示从现在开始 1 秒后的 dispatch_time_t
类型的值。
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
dispatch_after 实现
可以看到 dispatch_after
其实就是通过 Dispatch Source
实现的延迟执行 block
任务
void
dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *ctxt,
dispatch_function_t func)
{
_dispatch_after(when, queue, ctxt, func, false);
}
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_after(dispatch_time_t when, dispatch_queue_t dq,
void *ctxt, void *handler, bool block)
{
dispatch_timer_source_refs_t dt;
dispatch_source_t ds;
uint64_t leeway, delta;
if (when == DISPATCH_TIME_FOREVER) {
#if DISPATCH_DEBUG
DISPATCH_CLIENT_CRASH(0, "dispatch_after called with 'when' == infinity");
#endif
return;
}
delta = _dispatch_timeout(when);
if (delta == 0) {
if (block) {
return dispatch_async(dq, handler);
}
return dispatch_async_f(dq, ctxt, handler);
}
leeway = delta / 10; // <rdar://problem/13447496>
if (leeway < NSEC_PER_MSEC) leeway = NSEC_PER_MSEC;
if (leeway > 60 * NSEC_PER_SEC) leeway = 60 * NSEC_PER_SEC;
// this function can and should be optimized to not use a dispatch source
ds = dispatch_source_create(&_dispatch_source_type_after, 0, 0, dq);
dt = ds->ds_timer_refs;
dispatch_continuation_t dc = _dispatch_continuation_alloc();
if (block) {
_dispatch_continuation_init(dc, dq, handler, 0, 0);
} else {
_dispatch_continuation_init_f(dc, dq, ctxt, handler, 0, 0);
}
// reference `ds` so that it doesn't show up as a leak
dc->dc_data = ds;
_dispatch_trace_item_push(dq, dc);
os_atomic_store2o(dt, ds_handler[DS_EVENT_HANDLER], dc, relaxed);
dispatch_clock_t clock;
uint64_t target;
_dispatch_time_to_clock_and_value(when, false, &clock, &target);
if (clock != DISPATCH_CLOCK_WALL) {
leeway = _dispatch_time_nano2mach(leeway);
}
dt->du_timer_flags |= _dispatch_timer_flags_from_clock(clock);
dt->dt_timer.target = target;
dt->dt_timer.interval = UINT64_MAX;
dt->dt_timer.deadline = target + leeway;
dispatch_activate(ds);
}
网友评论