什么是将 C 样式 API 转换为将 libdispatch 与块一起使用的灵活方法
What is a flexible method to convert a C style API to use libdispatch with blocks
我有一个用 C 编写的现有 C API,它大量使用从函数中编辑的状态代码 return 来进行错误处理。我正在尝试了解使用 libdispatch 处理此类情况的首选方法。
这是一个示例函数。 RETURN_ERROR 和相关的宏只是调用一个函数,该函数 return 是一个错误代码并打印一条消息。
int
mport_db_prepare(sqlite3 *db, sqlite3_stmt **stmt, const char * fmt, ...)
{
va_list args;
char *sql;
va_start(args, fmt);
sql = sqlite3_vmprintf(fmt, args);
va_end(args);
if (sql == NULL)
RETURN_ERROR(MPORT_ERR_FATAL, "Couldn't allocate memory for sql statement");
if (sqlite3_prepare_v2(db, sql, -1, stmt, NULL) != SQLITE_OK) {
SET_ERRORX(MPORT_ERR_FATAL, "sql error preparing '%s': %s", sql, sqlite3_errmsg(db));
sqlite3_free(sql);
RETURN_CURRENT_ERROR;
}
sqlite3_free(sql);
return MPORT_OK;
}
我想在 sql 访问位周围使用串行队列,并且知道我可以使用 dispatch_sync
到 return 值。但是,我还读到很多同步调用很容易陷入死锁。
这种界面的最佳做法是什么?传递一个带有 success/fail 处理程序的块?我会提供一个 dispatch_queue_t
参数让完成块 运行 在用户指定的队列上吗?
这是您可以使用的示例模板。这是我的首选方法。我直接在浏览器中输入此内容,因此可能存在一些小错误(请编辑):
typedef void (^completion_block_t)(int error_code);
static dispatch_once_t init_once;
static dispatch_queue_t db_queue;
void perform_init(void __unused *ctx) {
db_queue = dispatch_queue_create("com.mycompany.db_queue", DISPATCH_QUEUE_SERIAL);
}
void perform_some_async_operation(dispatch_queue_t completion_queue, completion_block_t completion_block) {
dispatch_once_f(&init_once, NULL, perform_init);
if (completion_block) {
completion_block = Block_copy(completion_block)
if (!completion_queue) {
completion_queue = dispatch_get_global_queue(qos_class_self(), 0);
}
}
dispatch_async(db_queue, ^{
int error_code = 0;
...
if (completion_block) {
dispatch_async(completion_queue, ^{
completion_block(error_code);
});
}
});
}
请注意,如果您还想提供 API 的同步版本,最好使用它来实现您的异步版本,而不是相反。这样,系统可以更好地跟踪优先级继承的依赖关系。例如:
int perform_some_sync_operation() {
dispatch_once_f(&init_once, NULL, perform_init);
__block int error_code = 0;
dispatch_sync(db_queue, ^{
...
});
return error_code;
}
void perform_some_async_operation(dispatch_queue_t completion_queue, completion_block_t completion_block) {
dispatch_queue_t execution_queue = dispatch_get_global_queue(qos_class_self(), 0);
if (completion_block) {
completion_block = Block_copy(completion_block)
if (!completion_queue) {
completion_queue = execution_queue;
}
}
dispatch_async(execution_queue, ^{
int error_code = perform_some_sync_operation();
if (completion_block) {
dispatch_async(completion_queue, ^{
completion_block(error_code);
});
}
});
}
如果您的代码预计在 Yosemite 或 iOS 8 之前的系统上 运行,您将需要在 qos_class_self() 不可用时进行回退.例如:
completion_queue = dispatch_get_global_queue(&qos_class_self ? qos_class_self() : DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
我有一个用 C 编写的现有 C API,它大量使用从函数中编辑的状态代码 return 来进行错误处理。我正在尝试了解使用 libdispatch 处理此类情况的首选方法。
这是一个示例函数。 RETURN_ERROR 和相关的宏只是调用一个函数,该函数 return 是一个错误代码并打印一条消息。
int
mport_db_prepare(sqlite3 *db, sqlite3_stmt **stmt, const char * fmt, ...)
{
va_list args;
char *sql;
va_start(args, fmt);
sql = sqlite3_vmprintf(fmt, args);
va_end(args);
if (sql == NULL)
RETURN_ERROR(MPORT_ERR_FATAL, "Couldn't allocate memory for sql statement");
if (sqlite3_prepare_v2(db, sql, -1, stmt, NULL) != SQLITE_OK) {
SET_ERRORX(MPORT_ERR_FATAL, "sql error preparing '%s': %s", sql, sqlite3_errmsg(db));
sqlite3_free(sql);
RETURN_CURRENT_ERROR;
}
sqlite3_free(sql);
return MPORT_OK;
}
我想在 sql 访问位周围使用串行队列,并且知道我可以使用 dispatch_sync
到 return 值。但是,我还读到很多同步调用很容易陷入死锁。
这种界面的最佳做法是什么?传递一个带有 success/fail 处理程序的块?我会提供一个 dispatch_queue_t
参数让完成块 运行 在用户指定的队列上吗?
这是您可以使用的示例模板。这是我的首选方法。我直接在浏览器中输入此内容,因此可能存在一些小错误(请编辑):
typedef void (^completion_block_t)(int error_code);
static dispatch_once_t init_once;
static dispatch_queue_t db_queue;
void perform_init(void __unused *ctx) {
db_queue = dispatch_queue_create("com.mycompany.db_queue", DISPATCH_QUEUE_SERIAL);
}
void perform_some_async_operation(dispatch_queue_t completion_queue, completion_block_t completion_block) {
dispatch_once_f(&init_once, NULL, perform_init);
if (completion_block) {
completion_block = Block_copy(completion_block)
if (!completion_queue) {
completion_queue = dispatch_get_global_queue(qos_class_self(), 0);
}
}
dispatch_async(db_queue, ^{
int error_code = 0;
...
if (completion_block) {
dispatch_async(completion_queue, ^{
completion_block(error_code);
});
}
});
}
请注意,如果您还想提供 API 的同步版本,最好使用它来实现您的异步版本,而不是相反。这样,系统可以更好地跟踪优先级继承的依赖关系。例如:
int perform_some_sync_operation() {
dispatch_once_f(&init_once, NULL, perform_init);
__block int error_code = 0;
dispatch_sync(db_queue, ^{
...
});
return error_code;
}
void perform_some_async_operation(dispatch_queue_t completion_queue, completion_block_t completion_block) {
dispatch_queue_t execution_queue = dispatch_get_global_queue(qos_class_self(), 0);
if (completion_block) {
completion_block = Block_copy(completion_block)
if (!completion_queue) {
completion_queue = execution_queue;
}
}
dispatch_async(execution_queue, ^{
int error_code = perform_some_sync_operation();
if (completion_block) {
dispatch_async(completion_queue, ^{
completion_block(error_code);
});
}
});
}
如果您的代码预计在 Yosemite 或 iOS 8 之前的系统上 运行,您将需要在 qos_class_self() 不可用时进行回退.例如:
completion_queue = dispatch_get_global_queue(&qos_class_self ? qos_class_self() : DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);