概念介绍
event source:系统事件源,例如:unix信号,文件描述符,定时器等
dispatch source:是底层事件源的封装,可以理解跟事件源是同义词
创建 dispatch source
dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue);
- type:事件类型,比如:
DISPATCH_SOURCE_TYPE_PROC
DISPATCH_SOURCE_TYPE_READ
DISPATCH_SOURCE_TYPE_SIGNAL
DISPATCH_SOURCE_TYPE_TIMER
DISPATCH_SOURCE_TYPE_VNODE
DISPATCH_SOURCE_TYPE_WRITE
- handle:句柄,比如:fd, pid, signo, mach port 等
- mask:事件掩码
- queue:事件处理器执行的队列
配置 dispatch source
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue);
dispatch_source_set_event_handler(source, ^{
size_t size = dispatch_source_get_data(source);
read(fd, buf, size);
});
dispatch_source_set_cancel_handler(source, ^{
close(fd);
});
dispatch_resume(source); // 启动
install 事件处理器
dispatch_source_set_event_handler(source, ^{
size_t size = dispatch_source_get_data(source);
read(fd, buf, size);
});
install cacnel handler
dispatch_source_set_cancel_handler(source, ^{
close(fd);
});
uninstall 事件处理器
dispatch_source_cancel(source);
uninstall 事件处理器会调用通过dispatch_source_set_cancel_handler
函数安装的cancel handler
销毁 dispatch source (非arc)
dispatch_release(source);
从 dispatch source 获取数据
dispatch_source_get_handle
dispatch_source_get_data
dispatch_source_get_mask
改变队列
创建dispatch source时指定的队列可以被改变,使用函数
dispatch_set_target_queue
创建定时器
dispatch_source_t create_timer(dispatch_queue_t queue, uint64_t interval, dispatch_block_t block)
{
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer) {
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, (int64_t)interval), interval, 0);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
return timer;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_timer = create_timer(dispatch_get_main_queue(), 2 * NSEC_PER_SEC, ^{
// do something
});
}
延迟执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// do something
});
从文件描述符读数据
typedef dispatch_source_t file_reader_t;
file_reader_t file_reader_create(const std::string &path, void (^block)(const std::vector<uint8_t> &data, bool *stop))
{
int fd;
do {
fd = open(path.c_str(), O_RDONLY);
}
while (fd == -1 && errno == EINTR);
if (fd == -1)
return nullptr;
fcntl(fd, F_SETFL, O_NONBLOCK); // Avoid blocking the read operation
auto read_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
dispatch_source_set_event_handler(read_source, ^{
auto size = dispatch_source_get_data(read_source);
std::vector<uint8_t> buf(size);
auto n = read(fd, buf.data(), buf.size());
buf.resize(n);
bool stop = false;
block(buf, &stop);
if (stop)
dispatch_source_cancel(read_source);
});
dispatch_source_set_cancel_handler(read_source, ^{
close(fd);
});
dispatch_resume(read_source);
return read_source;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_file_reader = file_reader_create("/path/to/file", ^(const std::vector<uint8_t> &data, bool *stop) {
std::string s(data.cbegin(), data.cend());
std::cout << s;
});
}
这个用法类似:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// do something
}];
监控vnode
dispatch_source_t vnode_monitor_create(const std::string &path, dispatch_source_vnode_flags_t flags, std::function<void(int fd, bool *stop)> block)
{
int fd;
do {
fd = open(path.c_str(), O_EVTONLY);
}
while (fd == -1 && errno == EINTR);
if (fd == -1)
return nullptr;
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, flags, DISPATCH_TARGET_QUEUE_DEFAULT);
dispatch_source_set_event_handler(source, ^{
bool stop = false;
block(fd, &stop);
if (stop)
dispatch_source_cancel(source);
});
dispatch_source_set_cancel_handler(source, ^{
close(fd);
});
dispatch_resume(source);
return source;
}
监控unix信号
dispatch_source_t signal_monitor_create(int signo, std::function<void(int signo, bool *stop)> block)
{
// Make sure the signal does not terminate the application.
signal(signo, SIG_IGN);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, signo, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
dispatch_source_set_event_handler(source, ^{
bool stop = false;
block(signo, &stop);
if (stop)
dispatch_source_cancel(source);
});
dispatch_resume(source);
return source;
}
传统上,应用程序使用sigaction
函数来安装信号处理函数
gcd signal dispatch source 并不能代替传统的sigaction
gcd signal dispatch source 不能监控 SIGILL, SIGBUS, and SIGSEGV 等信号
gcd signal dispatch source 也不能阻止进程收到信号时的默认行为
通过sigaction
安装的信号处理函数是同步执行的,并且功能是受限制的
gcd signal dispatch source 的信号处理函数是异步执行的,并且功能不受任何限制
gcd signal dispatch source 的信号处理函数不会被其他信号处理函数覆盖
监控进程
dispatch_source_t process_monitor_create(pid_t pid, dispatch_source_proc_flags_t flags, std::function<void(pid_t pid, bool *stop)> block)
{
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, flags, DISPATCH_TARGET_QUEUE_DEFAULT);
dispatch_source_set_event_handler(source, ^{
bool stop = false;
block(pid, &stop);
if (stop)
dispatch_source_cancel(source);
});
dispatch_resume(source);
return source;
}
挂起和恢复
dispatch_suspend
dispatch_resume