GCD4: 用GCD执行与UI相关的任务
UI 相关的任务必须在主线程中执行,所以主队列是在 GCD 中执行 UI 任务的唯一候选对象。我们可以使用 dispatch_get_main_queue 函数得到处理主分派队列的句柄。
这里有 2 种向主队列分派任务的方法,两者都是异步的,即使在任务没有执行的时候也让你的程序继续:
dispatch_async 函数 在分派队列上执行一个 Block Object。
dispatch_async_f 函数 在分派队列上执行一个 C 函数。
(注意:dispatch_sync 方法不能在主队列中调用,因为这会无限期的阻止线程并会导致你的应用死锁。所有通过 GCD 提交到主队列的任务必须是异步的。 )
1.
我们看看 dispatch_async 函数的使用情况,它接受 2 个参数:
dispatch queue handle
在这分派队列上任务将被执行;
在这分派队列上任务将被执行;
Block object
为了异步执行 Block Object 会被发送到分派队列。
这有一个例子,在 iOS 中这段代码将通过主队列,向用户显示一个提示框:
//得到队列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); //给指定队列分派任务 dispatch_async(mainQueue, ^{ [[[UIAlertView alloc]initWithTitle:@"GCD" message:@"GCD is amazing!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]show]; });
是什么使这个主队列真正有意思呢?答案很简单:当你需要从 GCD 获取最高性能来在并行或者串行线程上完成一些密集运算时,你可能想给你的用户展示结果或者想把一个组件移动到屏幕上;为了做到这些,你必须使用主队列,因为它是与 UI 相关的工作。在使用 GCD 更新 UI 时,在这个部分展示的函数是离开一个串行或者并行队列的唯一办法,所以你能想到它有多重要了。
2.
在向主队列提交 Block Object 的执行话,你可以提交 C 函数来代替。在 GCD 中向 dispatch_async_f函数提交所有UI-related C函数的执行。
用 dispatch_async_f 函数我们可以提交一个 application-defined 的上下文的 指针,这个指针随后在调用 C 函数时用到。所以我们的计划是:我们先创建一个结构来保 存值,这些值包括一个警报视图的标题、消息和取消键的标题。当 APP 运行时,我们把这 些值放入结构并传递到 C 函数去展示。此处是我们如何定义我们的结构:
typedef struct{ char *title; char *message; char *cancelButtontitle; }AlretViewData;
现在我们来实现一下 C 函数,一会 GCD 会调用到这个函数。这个 C 函数应该期待 void*类型的参数,然后转换到 AlertViewData*。
//我们希望这个函数的调用者传递 给我们一个 alert view 的数据的引用,这个数据被封装在 AlertViewData 结构里: void displayAlertView(void *paramContext){ AlretViewData *alertData = (AlretViewData *)paramContext; NSString *title = [NSString stringWithUTF8String:alertData -> title]; NSString *message = [NSString stringWithUTF8String:alertData -> message]; NSString *cancelButtonTitle = [NSString stringWithUTF8String:alertData ->cancelButtontitle]; [[[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil] show]; free(alertData); }
注意:我们在此处而不是在调用方释放了传递给我们的 AlertViewData,原因在于调用方要异步执行这个 C 函数,但是不知道 C 函数何时执行结束。所以,调用方必须分配足够的空间给 AlertViewData,同时我们的 displayAlertView C 函数必须释放空间。
现在我们在主队列调用 displayAlertView 函数并把 Context(保存 Alert View 数据的结构)传递给它:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { dispatch_queue_t mainQueue = dispatch_get_main_queue(); /* malloc的全称是memory allocation,中文叫动态内存分配,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存。原型为extern void *malloc(unsigned int num_bytes)。 功能:分配长度为num_bytes字节的内存块 返回值:如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。函数返回的指针一定要适当对齐,使其可以用于任何数据对象。 备注: void* 表示未确定类型的指针,void *可以指向任何类型的数据,更明确的说是指申请内存空间时还不知道用户是用这段空间来存储什么类型的数据(比如是char还是int或者...) */ //所以这个代码的作用为动态申请AlretViewData大小的内存空间,将指向该内存空间的指针强制转换为AlretViewData * 赋值给context AlretViewData *context = (AlretViewData *)malloc(sizeof(AlretViewData)); if (context != NULL) { context -> title = "GCD"; context -> message = "GCD is amazing"; context -> cancelButtontitle = "OK"; dispatch_async_f(mainQueue, (void *)context, displayAlertView); } return YES; }
执行结果正常