使用 GCD 进行线程切换,主/背景 线程切换的最佳用例是?

2016-01-12 10:23:52 +08:00
 iOran

在背景线程请求完数据, block 异步带回需要的数据,此时,应该切换到主线程更新 UI 。更新 UI 可能是一大坨代码,所以我的做法是尽可能将这一坨封装成一个函数。更复杂一些,这个更新 UI 的函数的内部,又要异步去请求数据,请求完数据 block 回来又要切换回主线程更新 UI 。

最优的推荐做法,应该是进入这个更新 UI 的封装函数之前,切换到 main-thread;还是在这个封装函数内部最开始的位置,切换到 Main-thread?

我使用 Masonry 来布局,最近老是报:

This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.

我知道 layout 应该在主线程做,但难免,这里还是出了问题,那有没有什么最优做法,从编码习惯上就可以规范,保证线程切换不出错呢。所以有了如上问题。

3728 次点击
所在节点    iDev
21 条回复
kobe1941
2016-01-12 10:26:47 +08:00
你应该改设计,避免在更新 UI 的时候还要再去请求数据,多个数据请求可以用 dispatch_group ,请求都完成后再统一更新 UI
vincentxue
2016-01-12 10:48:34 +08:00
楼主最好贴点代码看看,我觉得你代码可能写的有问题。

是进入这个更新 UI 的封装函数之前,切换到 main-thread;还是在这个封装函数内部最开始的位置,切换到 Main-thread?

无论是苹果官方 Demo 还是开源库绝大多数都是用的第一种情况。
iOran
2016-01-12 10:56:27 +08:00
@kobe1941 那如果有些数据是要基于 **用户的选择** 或者 **不同的用户** 而请求的数据,还是有可能碰到这种情况吧?这里应该如何做?

既然说到 dispatch_group_async/notify ,我还想问问,如果加入到 queue 中的 **一系列任务** 是 **异步的**,此时, notify 到的 block 只是代表 group 已经将这 **一系列任务** 执行完,但它们(一系列任务)的异步执行结果 block 回来可能在 notify 之后,这种情况应该怎么处理?

例如这种情况,使用 group 真心爽:

// 获得系统提供的 Global Dispatch Queue 。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 创建 Dispatch Group ,注意跟 dispatch_queue_create 一样,在 MRC 环境下或者 iOS 6 之前的版本 ARC 环境下需要 用 dispatch_release(group)。

dispatch_group_t group = dispatch_group_create();

// 添加任务 Block 到任务队列 queue 中。
dispatch_group_async(group, queue, ^{NSLog(@"blk0");});
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});

// 通知 group ,那几个任务都结束了。” Done ”消息就属于押尾执行的任务。
dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"done");});

但是如果 **添加任务 Block 到任务队列 queue 中** 变成这样:

// 添加任务 Block 到任务队列 queue 中。
dispatch_group_async(group, queue, ^{异步任务 1 ,执行完执行 ^(parameters){} });
dispatch_group_async(group, queue, ^{异步任务 2 ,执行完执行 ^(parameters){} });
dispatch_group_async(group, queue, ^{异步任务 3 ,执行完执行 ^(parameters){} });

问题出现了:它们(异步任务 1 , 2 , 3)的异步执行结果 block 回来可能在 notify 之后。怎么破?
iOran
2016-01-12 10:59:47 +08:00
@vincentxue 谢谢了。我也侧重第一种情况。应该是我代码看得少了,应该找点开源库看看人家的实现。
aaaron7
2016-01-12 13:40:29 +08:00
都一样。你遇到这报错,八成是你代码问题。
aaaron7
2016-01-12 13:41:55 +08:00
@iOran
你爽点也太低了。

你去用 reactive cocoa ,那才叫爽。
iOran
2016-01-12 14:13:37 +08:00
@aaaron7 谢谢提醒,反过头会去看看。能否先帮忙顺便解答下 dispatch_group_async 中的任务,如果也是异步的情况下,如何保证 notify 押尾执行。
aaaron7
2016-01-12 14:21:19 +08:00
@iOran
GCD 用得不多,我想到的方法就是利用变量来 track 每一个任务的完成情况,每个任务执行完毕后都检查一次。

但是用 rac 就可以很优雅的实现这个逻辑:


// Performs 2 network operations and logs a message to the console when they are
// both completed.
//
// +merge: takes an array of signals and returns a new RACSignal that passes
// through the values of all of the signals and completes when all of the
// signals complete.
//
// -subscribeCompleted: will execute the block when the signal completes.

[[RACSignal
merge:@[ [client fetchUserRepos], [client fetchOrgRepos] ]]
subscribeCompleted:^{
NSLog(@"They're both done!");
}];
EPr2hh6LADQWqRVH
2016-01-12 14:21:21 +08:00
非 ios 程序员我 Google 了一下才了解到这个语境下 GCD 的意义。。
ios 开发论坛药丸的节奏
raysonx
2016-01-12 14:31:56 +08:00
@avastms +23333333333333
標題被屏蔽為「使用 *** 进行线程切换,主 /背景 线程切换的最佳用例是?」就笑尿了
loveuqian
2016-01-12 14:34:34 +08:00
@avastms
哈哈哈哈哈哈哈
huangweihua
2016-01-12 14:44:25 +08:00
```
/**
主线程回调

- parameter block: 执行 Block
*/
func dispatch_async_safely_main_queue(block: ()->()) {
if NSThread.isMainThread() {
block()
} else {
dispatch_async(dispatch_get_main_queue()) {
block()
}
}
}
```
vincentxue
2016-01-12 14:50:59 +08:00
@iOran 你把 group 用错了,但你这样用也不能算错。

你加进去的三个 nslog 是同步任务,那你直接执行或者直接放在一个队列里就好了,没必要用 group 。

如果是异步操作,应该配合 group 的 enter/leave 方法使用,这样才能达到你要的效果。
hzm0318hzm
2016-01-12 14:51:40 +08:00
@iOran 这样异步使用 group 的话使用 dispatch_group_enter()/dispatch_group_leave() 看你的异步队列到最后能不能执行你收尾的函数
vincentxue
2016-01-12 14:53:10 +08:00
@huangweihua 你的方法如果 block 传入 nil 会怎样? oc 是肯定 crash 了。
iOran
2016-01-12 15:54:08 +08:00
@aaaron7 Merge 的对象,不管时异步/同步都是一样吗?
iOran
2016-01-12 15:56:37 +08:00
@vincentxue @hzm0318hzm 谢谢两位,知道怎么操作了。后续补答案。

@vincentxue 三个 nslog 是同步任务是考虑到这些同步任务无所谓执行的先后关系,所以这么丢。
LGA1150
2016-01-12 21:41:40 +08:00
@raysonx 几年前贴吧确实屏蔽了这个关键字,不过没有影响到标题
iOran
2016-01-13 09:33:32 +08:00
@LGA1150 原来这些人是这个意思。。。。
huangweihua
2016-01-27 17:51:54 +08:00
@vincentxue 没做测试,传入 nil , nil send_msg 不会 crash 。 swift 的话,后边没有加 ?,所以没办法传入 nil

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/250067

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX