首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >RunLoop对DispatchQueue的调度

RunLoop对DispatchQueue的调度
EN

Stack Overflow用户
提问于 2019-06-23 13:55:17
回答 5查看 9.7K关注 0票数 48

当使用新的组合框架时,可以指定从发布服务器接收元素的调度程序。

当将publisher分配给UI元素时,RunLoop.mainDispatchQueue.main之间有很大的区别吗?第一个返回主线程的run循环,以及与主线程关联的第二个队列。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2019-07-06 03:40:53

我在Swift论坛上发布了类似的问题。我鼓励大家看看https://forums.swift.org/t/runloop-main-or-dispatchqueue-main-when-using-combine-scheduler/26635的讨论。

我只是复制并粘贴来自Philippe_Hausler的答案

RunLoop.main作为调度器,最终调用RunLoop.main.perform,而DispatchQueue.main调用DispatchQueue.main.async来工作,实际上它们几乎是同构的。唯一真正的区别是,RunLoop调用最终在RunLoop标注中的不同位置执行,而如果lib分派中的优化启动,DispatchQueue变量可能会立即执行。在现实中,你永远不应该看到两者之间的区别。 RunLoop应该是当您有一个运行RunLoop的专用线程时,DispatchQueue可以是任何队列场景(为了记录,请避免在DispatchQueues中运行RunLoops,它会导致一些非常糟糕的资源使用.)。此外,值得注意的是,用于调度程序的DispatchQueue必须始终是串行的,才能遵守Combine的运算符的约定。

票数 12
EN

Stack Overflow用户

发布于 2020-04-08 18:41:02

实际上,使用RunLoop.main作为Scheduler与使用DispatchQueue.main作为Scheduler是有很大区别的。

  • RunLoop.main只在主运行循环在.default模式下运行时才运行回调,这是而不是--跟踪触摸和鼠标事件时使用的模式。如果将RunLoop.main用作Scheduler,则当用户处于触摸或拖动过程中时,事件将不会被传递给
  • DispatchQueue.main在所有.common模式中运行回调,其中包括跟踪触摸和鼠标事件时使用的模式。如果使用DispatchQueue.main,则事件将传递给,而使用用户则在触摸或拖动的中间。

详细信息

我们可以看到RunLoopSchedulers+RunLoop.swift中对Scheduler的一致性的实现。

public func schedule(options: SchedulerOptions?, \_ action: @escaping () -> Void) { self.perform(action) }

这使用了RunLoop perform(_:)方法,这是Objective方法-[NSRunLoop performBlock:]performBlock:方法将块调度为只在默认运行循环模式下运行。(这没有记录在案。)

UIKit和AppKit在空闲时以默认模式运行run循环。但是,特别是在跟踪用户交互时(例如按下触摸或鼠标按钮),它们在不同的非默认模式下运行run循环。因此,使用receive(on: RunLoop.main) 的组合管道不会在用户触摸或拖动时传递信号。

我们可以看到DispatchQueueSchedulers+DispatchQueue.swift中符合Scheduler的实现。

public func schedule(options: SchedulerOptions?, \_ action: @escaping () -> Void) { let qos = options?.qos ?? .unspecified let flags = options?.flags ?? [] if let group = options?.group { // Distinguish on the group because it appears to not be a call-through like the others. This may need to be adjusted. self.async(group: group, qos: qos, flags: flags, execute: action) } else { self.async(qos: qos, flags: flags, execute: action) } }

因此,使用标准的GCD方法异步(组:qos:标志:execute:)将块添加到队列中。在什么情况下执行主队列上的块?在普通的UIKit或AppKit应用程序中,主运行循环负责排空主队列。我们可以在CFRunLoop.c中找到run循环实现。重要的函数是__CFRunLoopRun,它太大了,不能全部引用。这里是兴趣线

#if __HAS_DISPATCH__ __CFPort dispatchPort = CFPORT_NULL;Boolean = pthread_main_np() && ( (HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) \x (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY &0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ );如果( libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes,rlm->_name) dispatchPort = _dispatch_get_main_queue_port_4CF();#endif

(为了提高可读性,我已经包装了原始源代码行。)这段代码所做的是:如果释放主队列是安全的,它是主运行循环,并且是.common模式,那么CFRunLoopRun将检查主队列是否准备就绪。否则,它将不进行检查,因此不会耗尽主队列。

.common模式包括跟踪模式。因此,使用receive(on: DispatchQueue.main) 的组合管道将在用户触摸或拖动时传递信号。

票数 75
EN

Stack Overflow用户

发布于 2019-11-14 03:39:05

我看到罗伊的回复,认为我可以互换使用,但实际上我注意到我的应用程序有很大的不同。

我正在自定义表视图单元格中异步加载一个图像。只要表视图正在滚动,使用RunLoop.main就会阻止图像的加载。

代码语言:javascript
运行
复制
  subscriber = NetworkController.fetchImage(url: searchResult.artworkURL)
    .receive(on: RunLoop.main)
    .replaceError(with: #imageLiteral(resourceName: "PlaceholderArtwork"))
    .assign(to: \.image, on: artworkImageView)

但是切换到DispatchQueue.main允许在滚动时加载图像。

代码语言:javascript
运行
复制
  subscriber = NetworkController.fetchImage(url: searchResult.artworkURL)
    .receive(on: DispatchQueue.main)
    .replaceError(with: #imageLiteral(resourceName: "PlaceholderArtwork"))
    .assign(to: \.image, on: artworkImageView)
票数 32
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56724566

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档