›也可以 在此链接的我的博客上找到本文 。 计时器允许我们在一个或多个计时器间隔后执行一些代码。 有多种类型的时钟可用于创建计时器,即使所有这些时钟显然都以相同的速率运行,它们的行为仍然不同。 我们可以列出以下计时器类型列表: 实时时钟或RTC 。 它是一个计算机时钟(通常为集成电路形式),用于跟踪当前时间。 用户可以任意更改此时钟,而NTP(网络时间协议)则最好使其与外部参考保持同步。 它的值每秒增加一秒钟,但有时可能会更快/更慢或向前跳跃( 这要感谢Gavin Eadie,这使我意识到时钟永远不会向后运行.NTP同步发现时钟在运行如果运行速度快,则在尝试与外部源同步时应放慢速度,直到“实时”赶上 。 单调计时器 。 它的计数器通过使用计时器中断发送到CPU的物理信号递增。 在Apple平台上,此值由Mach内核通过mach_absolute_time()返回。 返回值取决于CPU,因此您不能只将其乘以一个常数就可以得出真实值。 相反,您应该调用系统提供的转换函数将其转换为真实值(CoreAnimation有一个方便的方法:CACurrentMediaTime())。 它在启动时被重置的事实使得获取现实世界中流逝的时间并不那么有趣,但是它是测量两个时间间隔之间差异的最精确方法。 启动计时器 。 它只是一个特殊的单调计时器 ,在系统进入睡眠状态时不会暂停。 获取其值的最常见方法是从终端调用uptime函数。 在Apple平台上,创建计时器的最常见方法是使用NSTimer类。 实际上,它只是围绕单调计时器的包装。 因此,使用NSTimer可能会以无法预料的方式结束,特别是在iOS上,在如上所述的某些极端情况下,机会性的资源使用可能会结束。 为了充分理解NSTimer,我们需要谈一些NSRunLoop; 一旦启动,每个应用程序都会创建第一个NSThread,称为Main Thread; 每个线程都有一个关联的运行循环,该循环管理输入源,例如鼠标,键盘,触摸,连接……以及显然是我们的计时器。 您可以将RunLoop视为等待新消息并将其传递给适当的收件人的邮箱 :它基本上是一种消息传递机制,用于异步或线程间通信。 一些平台(如Windows)将其称为Message Pump,但内部概念仍然相同。 实际上, 运行循环代表了命令行 应用程序 与交互式 (通常是基于UI) 应用程序 之间的主要区别 。 当第一个使用参数启动时,执行它们的内容,然后退出,一个交互式应用程序等待用户输入,对此做出反应并再次等待。 每个线程只有一个Run循环; 一个运行循环由一组要监视的输入源(键盘,触摸等)和一组要通知的观察者组成。 隐式或显式地使用特定的运行模式初始化运行循环。 在其生命周期内, 仅监视与该模式关联的源并允许其传递事件; 仅将与该模式关联的观察者通知新数据。 Cocoa / UIKit定义了几种类型的模式:在iOS上,有一种特殊的模式称为UITrackingRunLoopMode:在控件中进行跟踪时设置。 […]
所以我从最近2年开始使用objC,从最近4个月开始使用Swift 3。 因为这是我的第一个博客,所以这个问题对我来说很难解决。 在尝试了多个stackOverFlow线程之后,在搜索了数百篇博客之后,我终于找到了解决方案。 就是这样 问题: 考虑一个具有UICollectionView作为情节提要的IBOutlet的视图控制器。 它将解析后的JSON响应对象用作数据源数组。 因此JSON结构非常庞大,就像我们的数据源一样。 看一下下面的UI截图: 所以UICollectionView具有问题和答案的结构,因为标题显示了问题,而单元格是可用于相应答案的多个答案。 单元格包含一个UIImageView和UILabel作为描述。 用户必须至少选择一个单元格作为一个问题的答案,否则,UICollectionView将滚动到特定的索引单元格。 为了存储选定的答案,我为选定的索引维护了[[IndexPath]]。 这样我就可以知道用户选择了哪个值,并将此数组用于cellForItem以显示所选答案。 滚动到特定索引单元格的代码: 现在,最关键的部分是我花了很多天来弄清楚的。 在单击下一个按钮时,UICollectionView应该可以平滑滚动到相应的索引单元,但是在滚动时会被切碎。 我不得不点按两次按钮三下才能到达该特定的索引单元格。 调试: 所以,首先我可能是因为分层。 因为我是CALayer 与所选单元格接壤以突出显示特定单元格。 但是即使去除分层后,问题仍然存在。 其次,我认为这一定是有可能发生的,因为我没有正确地缓存图像 ,所以一定是因为这个原因,但是即使在调试代码后,我仍然知道这不是问题,这是不同的。 花了很多时间后,我有了使用XCode的工具来了解问题的想法。 因此,使用仪器后,我开始获得清晰的图像。 当我单击按钮进行验证时,我的应用程序没有足够的内存。 即我的整个主线程因执行该特定任务而被阻塞。 当我在主线程上进行分层,图像下载以及UICollectionView的滚动时,主线程被100%的workLoad使用。 解: 因此,为了解决这个问题,我创建了一个VC中的另一个线程,在我的viewDidLoad 中将 QoS指定为.userInteractive 。 因此,上面的代码所做的只是创建另一个线程并从主线程共享工作负载,以便更好地响应主线程。 现在,主线程只有两个任务可以执行:渲染边框和显示图像。 当我们使用此scrollDispatchQueue滚动到相应的索引时,单击下一步按钮时,现在不会出现UICollectionView滚动的斩波。 在解决这个问题的过程中,我非常想解决这个问题,但是现在终于解决了,并且知道我们可以使用GCD在多线程中发挥什么作用。 所以希望这个博客能在以后迅速帮助像我这样的新手3 3 因为这是我的第一个博客,请忽略是否有错字。 欢迎对此发表评论或提供任何帮助,或针对相应问题提出其他建议。 真的很高兴知道。 感谢您的阅读,祝您编程愉快。
GCD或运营 每当我问这两种排队任务方法之间的区别时,我都会得到类似的响应: 随便使用GCD 使用GCD进行简单的单个异步调用 但在以下情况下使用“ 操作” 你想取消他们 您想要一次执行更多任务 当您要在依赖关系中执行它们时 当您想优先执行时 这些建议听起来像GCD会缺少Operations的某些核心功能,但有人反复说Operations或多或少是围绕GCD的包装。 好吧,后者当然是正确的,而且运营似乎没有什么特别之处,您对GCD所做的事情实际上并不多,实际上,很多事情都以一种更轻松的方式,更易读,尽管个人偏见可能会起到一定的作用。与我们生活中的一切息息相关。 我可以在某种程度上说,Operations不能完全实现通过封装进行简单分配的任务,因为至少就我个人而言,仅使用GDC似乎更容易实现太多事情。 误解 但是,我认为应该对操作存在一些真正的误解: 1.取消应用程序并不会发生。 开发人员必须实现取消并在属性上设置KVC才能实现这一点, OperationQueue只是侦听它。 取消的实现方式与您在GCD中执行的方式相同。 这里没有魔术! 例如,如果需要取消异步URLSession ,则必须使用KVC更改状态以通知OperationQueue ,但是您仍然必须自己实现取消URLSession 。 实际上,您在这里还需要进行其他操作。 2.也可以使用 DispatchGroup 轻松地原子地执行任务组 ,这并不是OperationQueues能够单独执行的,实际上,使用GCD 可以使任务组更短,更容易且更精简。 3.再次依赖,当使用OperationQueue时,不要只是开箱即用。 需要预先将操作链接为从属链接,并完成isFinish的所有开销,并且需要实现其余样板,以便该机制起作用。 我会说,在GCD的串行队列上使用Dispatch更具可读性,更容易理解,特别是在存在更多后续任务的情况下。 但是,如果要同步的任务本身本质上是异步的,则GCD变得更加易于使用。 如果只有一个附加的依赖关系,那么我们可以嵌套第二个任务。 如果还有更多,那么使用DispatchGroups是一件很容易的事情…… 当依赖关系可能在单个任务和任务组之间时,还有很多要显示的东西,这就是GCD在操作性方面比操作性更出色的地方。 不过,这并不是经常发生的用例,因此,这并不是一个巨大的优势。 缺点 但是,恕我直言,使用Operations存在一些实际缺点,我不想重复样板代码的开销,我相信这是大多数人在比较这些方法时唯一提到的事情。 这里还有更多重要的事情要提到: Operations是NSOperations ,正在为所有子类带来动态分配。 目前还没有Swift的对手,并且有理由相信不会因为这里提到的接下来的几件事 他们使用KVC和KVO ,这在Swift中仍然是未知的概念,并且极不可能在不久的将来实现。 我们没有Operation结果的无状态返回,因为Operation的completementBlock无效。 因此,我们需要在Operation上创建可读的属性(状态),以便将结果传回。 任何一种附加状态都会导致附加的内存管理复杂性 Operations可以吸引开发人员“统一”使用它们,即使是琐碎的任务,也可以包装简单的代码以“遵循方法”…… 单元测试中的复杂性更高,需要制作一些帮助程序OperationQueue模拟以能够正确测试它 有时它们只是成为所有业务逻辑的转储,几乎违反了所有SOLID原则。 如果有的话, Operations应该大部分都是样板,仅带有1+个方法调用和1+个结果属性,恕我直言。 同样,在观看WWDC […]
要创建吸引用户每天使用它的iOS应用,精美的设计必不可少,因此应用中的快速体验也至关重要。 几个月前,我下载了一个教您如何烹饪的应用程序。 我在使用它的10分钟内删除了它。 为什么? 图片无法加载,我被卡在加载标牌上很长时间(例如1-2分钟?)。 这很令人沮丧,因为我想看看教程中的菜式。 好吧,这没有发生。 那么到底需要发生什么呢? 排队是有益的,也是麻烦的 iPhone内有一个CPU(中央处理单元)。 CPU一次只能执行一项任务。 待处理的任务排成一行 。 将排队视为排队等候餐厅座位的人。 当服务员有空位时,将排在第一位。 先进先出(FIFO)。 那么,我们的手机又如何如此迅速地响应我们的触摸和动作? 答案是:多线程。 多线程允许处理器创建并发线程以便在处理器之间进行切换。 结果,可以同时执行多个任务。 每个线程都有自己的队列。 就像剧院票房的台词一样。 一条线供客户购买机票,另一条线供客户打印预订的机票。 人们在每一行同时进出。 相同的概念适用于iOS生态系统。 如果我们在应用程序内部具有多线程,则不会发生像烹饪应用程序这样的悲剧。 我们不必从服务器获取配方,而不必等待它完成然后显示图像。 让我们看看我们如何做到这一点! 在iOS中引入Grand Central Dispatch(GCD) Grand Central Dispatch将任务分配给不同的线程(不同的队列)。 例如,主线程负责从服务器获取配方数据,而后台线程将更新UI并显示图像。 因此,该应用程序保持响应状态。 很好,对吗? GCD API围绕 DispatchQueue 。 此类使我们能够创建/修改队列,以及同步或异步分配代码 和更多。 同步方式 为了。 当您执行同步操作时,以后出现的所有内容都必须等待上一个操作完成才能开始。 另一方面,当您异步执行某项操作时,以下代码可以立即运行。 在其他队列上启动任务后,它将立即将控件返回到当前队列,而无需等待其结束。 异步方法不会阻止当前队列上的任务执行。 如您所见,当队列给我们带来麻烦时,我们可以通过它的变体来巧妙地处理它。 请继续关注第二部分–代码中的GCD实现!
要从本地/服务器文件加载数据,应具备swift知识,iOS App开发,View-controller和tableview的知识。这篇文章将分为两部分,第一部分我们在本地获取数据,第二部分将从服务器获取数据。逐步指导 我们将首先设置项目,只有一个视图控制器包含一个表视图,我们还将创建一个名为DataLoader的单独服务,该服务将从本地文件或服务器获取数据,为简化起见,全班将遵循单例模式 2.在ViewController.swift中,我们将设置IBOutlets,并将其设置为tableview的数据源。 在viewWillApper中,我们调用getNames函数,该函数将依次调用DataLoader服务函数getNamesFromServer / getNames,此函数需要一个完成块,因此我们向其传递PopulateTable函数 3. PopulateTable我们检查是否成功请求,如果不是,则输出错误,如果成功,则从收到的响应中设置名称,并在tableview上调用reload 4.将txt文件添加到您的项目中,其中一些名称以json格式显示 5.在DataLoader.swift中,我们创建一个函数getNames 首先,我们获得文件的路径,该文件在本地添加,然后将其转换为URL的实例,并初始化名称数组 6.我们将优先级设置为后台的DispatchQueue.global称为异步后台程序,我们将其称为并发后台队列,因为我们不知道加载数据将花费多少时间,直到那时我们无法停止执行主线程这样做,我们的应用似乎会挂在二手设备上,给用户带来缓慢的用户体验 7.我们将url的内容转换为DATA对象,然后使用JSONSerialization类将其反序列化,然后转换为json对象 8.我们将把这个json对象解析为字典(因为这样做了)并提取名称(在数组中一个接一个地添加它们) 9.我们现在将在Main队列上调用完成处理程序,因为任何UI更改都必须在主队列上进行,否则应用程序将崩溃(或发生意外行为),或者如果上述任何步骤失败,我们将使用调用完成处理程序成功=错误 10.运行应用 5在DataLoader.swift中,我们创建函数getNamesFromServer 我们创建一个URL对象,获得URLSession.shared会话,然后使用刚创建的url启动dataTask 6.我们检查是否获取了数据,如果我们从服务器接收到数据,我们将以与第1部分相同的方式进行处理,并相应地调用完成处理程序 7.运行应用 很少有问题需要回答 Q1 @转义是什么? 答:我们希望完成对象不在函数getNames / getNamesFromServer的范围内,因此我们使用@escaping表示法来告诉编译器相同的对象 Q2为什么我们在Part2中不使用DispathQueue.global? 答:我们没有用相同的方法创建后台队列,因为URLSession自己处理它,它在内部剪切后台队列以发出http请求,我们只需要处理主队列即可。 Q3我们从哪里获得此服务器? A.上有一个网站http://jsonplaceholder.typicode.com/可以访问此网站以获取不同类型的Json数据
众所周知,如果没有并发编程或多个CPU,愚蠢的计算机一次不能完成一项任务 点击此处获取示例应用 介绍 : 并发在编程语言中是一个巨大的话题,并且有点低级。 在本文中,我将讨论并行编程。 有很多用于并发的API.Apple发布了两个用于并发编程NSOperation和Dispatch Queue的低标签API。我将介绍Grand Canter Dispatch(GCD),并探讨我们为什么需要它以及如何使用Swift来实现它,并希望您能精通在这个API上..所以,让我们一起摇滚吧.. 那么为什么我们需要并发: 我们都知道计算机(单核)不能一次执行多任务。但是在计算中,多任务是必不可少的。因此,为了解决这个问题,发明了并发概念。并发是一次执行多任务的方式。 在高度上:考虑我们有一个应用程序,它一次有两个任务,一个是从网络下载数据,另一个是更新UI。两个任务都需要同时执行。如果下载(网络请求)需要一些时间,例如30此时用户界面将停止更新。并且应用程序将冻结甚至被压碎。从用户角度来看,它的作用各不相同。没有用户会再次使用此应用程序。 我们需要并发编程。 所以 在深入探讨并发之前,我们需要回顾一些定义,这将有助于我们进一步了解: 任务可以串行或同时执行两种方式 串行:任务可以一个接一个地执行。一个任务在其上一个任务完成时开始启动,例如FIFO(先入先出)。 我们可以举一个例子,例如电影院售票柜台。 如果有一个柜台,所有顾客都排队。 它是一个串行队列。当一位顾客购买票时,他将不在排队,下一位顾客将来取票。 它是一个串行队列。 并发: 并发只是一个概念,它可以同时运行多个任务,这可以在单核CPU或多核CPU中发生。在单核CPU中,它是通过时间分片来实现的。一个线程先执行上下文切换,然后再运行另一个线程线程或多核CPU通过并行执行多个线程。 并发是两种类型: 1,并发无并行 2并行并行 线程: 线程是进程的子单元,换句话说,线程是由操作系统调度程序独立调度的任务组。 Queue列: 队列是按先进先出(FIFO)的顺序管理对象的数据结构。在我们的示例电影票客户线上是队列。 为什么我们需要并发? 由于一些非常重要的原因,我们需要并发: 始终响应UI :在任何iOS应用程序首次启动时,默认情况下都会运行一个主队列。主队列可以更新用户界面。因此,我们需要使此队列仅用于UI和后台队列中的其他繁重任务。因此,此处进行并行编程需要解决这个问题。 利用iOS设备:如今,iOS设备是多核处理器,通过并发编程,我们可以并行使用多核处理器。 释放到主队列 大坎特派遣(GCD): 因此,我们已经为并行编程涵盖了足够的主题.Apple有两个用于并行编程的API.NSOperation和GCD。最常用的是GCD(Grand Canter Dispatch)。我们可以通过此API在iOS和MacOS上管理多线程。同步和异步任务队列。 GCD有调度队列,用于按FIFO顺序一个接一个地管理所有队列。GCD提供两种类型的队列,分别是串行队列和并发队列。这两个队列可以同步运行,也可以异步运行。我们将在一段时间后讨论。 串行队列: 串行队列可确保在任何给定时间仅运行一项任务。 GCD控制执行时间。 我们不知道任务何时开始并最终由调度队列管理。 在此图中,所有正在运行的任务都是按一个接一个的顺序执行的任务1比tast 2完成tn thn任务3启动等等… 并发队列: 并发队列一次运行多个任务。 所有任务都是按顺序添加的,但我们不知道什么时候全部完成或一次要执行多少任务都由系统管理。 如图所示,任务1开始并且这次没有任务在运行,但是任务1和任务2在同一时间启动,但是任务1在任务2之前完成了,所以并发队列我们不知道任务什么时候完成或如何完成我们将花费很多时间,我们只是知道任务将按照我们的订购方式运行。 它的依赖于调度到多个任务的队列将在不同的内核上运行或通过上下文切换发生。但是通常,如果内核可用而不是在内核上发生,则通常使用上下文切换。 GCD提供了三种主要的队列类型: […]
多线程和并发对于现代应用程序是必不可少的……但是,Grand Central Dispatch是用于管理并发操作的系统级库,它具有iOS SDK中较为麻烦且不友好的API之一。 不再。 Swift 3带来了对Grand Central Dispatch语法和用法的许多改进。 这是一些新功能的快速浏览。 dispatch_async 以前,我们将选择调度方法(同步与异步),然后选择要向其调度任务的队列。 更新后的GCD会颠倒顺序-我们首先选择队列,然后应用调度方法。 最常见的GCD模式之一是在全局后台队列上执行工作,并在工作完成后立即更新主队列上的UI。 新API的外观如下: 队列属性 您会注意到,队列现在在init上具有属性。 这是一个Swift OptionSet,可以包括队列选项,例如串行与并发,内存和活动管理选项以及服务质量(.default,.userInteractive,.userInitiated,.utility和.background)。 服务质量取代了iOS8中不推荐使用的旧优先级属性。 如果您习惯了优先级队列,请按照以下方法将它们映射到QOS案例: * DISPATCH_QUEUE_PRIORITY_HIGH:.userInitiated * DISPATCH_QUEUE_PRIORITY_DEFAULT:.default * DISPATCH_QUEUE_PRIORITY_LOW:.utility * DISPATCH_QUEUE_PRIORITY_BACKGROUND:.background 内存和活动管理选项是今年的Apple OS版本(OSX 10.12,iOS 10.0,tvOS 10.0,watchOS 3.0)的新增功能。 这些功能包括使用.initiallyInactive在非活动状态下启动队列或使用.autoreleaseInherit,.autoreleaseNever和.autoreleaseWorkItem为队列设置自定义自动释放设置的功能。 工作项目 队列不是GCD获得Swift OptionSet的唯一部分。 工作项目也有更新的Swift语法: 现在,工作项可以在初始化时声明质量或服务和/或标志。 这两个都是可选的,并且会影响工作项的执行。 这些标志是一个选项集,其中包括以下选项:barrier,detached,assignCurrentContext,noQoS,InheritanceQoS,forceforceQoS。 一次派遣 dispatch_once对于初始化代码和仅执行一次的其他功能非常有用。 在Swift 3中,不建议使用dispatch_once,而应将其替换为全局或静态变量和常量。 dispatch_time_t dispatch_time_t是将指定时间转换为可以提供给队列的UInt64的函数。 更新的GCD为此引入了更友好的语法(告别NSEC_PER_SEC)。 这是一个使用after之后的示例: .seconds是名为DispatchTimeInterval的新枚举的一部分。 这些案例具有一个代表计数的关联值。 目前支持: * […]
在第一部分中,我们介绍了串行和并发队列以及示例。在这一部分中,我们将详细介绍有关具有参数更改的并发队列的苹果,并学习如何创建全局队列,非活动队列,时间延迟,死锁。 让我们从我们离开的地方开始。 InactiveQueue: 在attribute参数中有另一个值initialInactive。因此,我们可以首先使非活动队列成为队列,当需要活动时,我们可以使它处于活动状态。 让我们将属性值并发更改为initialInactive,如下所示: let newQueue = DispatchQueue(标签:“ com.concurrent.ekram”,qos:DispatchQoS.background,属性:.initiallyInactive) 并在并发函数的外部块中创建DispatchQueue的类属性: var inactiveQueue:DispatchQueue! 现在在这样的函数中使用newQueue初始化不活动队列: inactiveQueue = newQueue 现在,newQueue处于非活动状态,并且viewDidAppear不知道队列,因此需要在viewDidAppear中手动激活,如下所示: 如果让队列= inactiveQueue { queue.activate() } 现在,代码将以串行方式运行并执行队列。请参见屏幕和控制台: 最初不活动时如何使用并发: 属性参数很简单,请同时使用.initiallyInactive和的数组。 并发比队列最初也将处于非活动状态并发。 let newQueue = DispatchQueue(标签:“ com.concurrent.ekram”,qos:DispatchQoS.background,属性:[。initiallyInactive,.concurrent]) 现在的结果是: 因此,当我们使用active()方法在viewDidApper上同时运行时,newQueue最初也处于非活动状态并且是并发的。 创建全局队列: 我们已经知道如何使用property创建自定义队列。 实际上,我们不需要创建全局队列,因为GCD工程师已经为我们创建了全局队列。因此,我们可以像这样创建.. 让globalQueue = DispatchQueue.global() 我们可以添加到全局队列方法 globalQueue.async { 对于i in 0 .. <10 { 打印(“ Custom Green Love:Custom”,i) } } […]
,로,글입니다。 。다있습니。 GCD的并发性和线程化。 스레드(thread)프로그램내에서내에서세다말한다。다。 만로한그램은스그램은가지있지만,만로그램이둘스상의있다。 이멀티멀티 스멀티 (多线程)한다。 主线程(UI)和后台线程,以及Apple的Grand Central Dispatch(GCD)和 NSOperatin Queue的产品。 이는thread를관리해야하는해준다。 已在GCD上添加了任务,然后在GCD上添加了GCD。 并发性。 。다수의이실행되는다。 프로세스나리어플이션은상의상의이그스상의가진다。 OS调度程序,然后单击“计划”。 使用时间片 ,使用并行处理。 능가능 race이race(比赛条件)방지 느림(모든이그이작업이끝나길기렸다렸기되) 并发队列。 。이순서대로실행됨을보장하지만이끝나는다없다。 불가불 빠름 들어,용자의사를를다고때다고할지할지않다않다。 。다。 并发队列。 할때는지를로순서가중요하다고串行队列를를다 GCD세가지메인공한공한공한공한공한.。 主队列 : 主线程 동작하며 串行队列 이다。 让mainQueue = DispatchQueue.main 2.全局队列 : 并行队列 이다。 高,默认,低,背景。 QOS(Quality of Service)类의로퍼티를지정정다。 GCD相片和影片。 let backgroundQueue = DispatchQueue.global(qos:DispatchQoS.QoSClass.background) […]
我确定您已经遇到过异步任务,并且必须使它们同步执行的情况,因为它们之间存在依赖关系。 想象一下安排会议的场景 调用一些登录API以获得令牌 获取特定时间的可用人员列表 得到一个人的细节 与有空人员开会 在您大声疾呼这只是API的不良示例之前,请放心,我同意您的观点。 但这是我们现实太多次了。 但是,有一个开箱即用的解决方案: 在队列上 使用 操作 和链接相关任务! 这听起来像是我们要实现的教科书示例。 让我们使用操作并使其起作用: 我们可以看到我们有一个Operation SumOfTwoAsyncOperations 。 我们可以清楚地看到它的业务逻辑在全局后线程队列上的异步调用中执行业务逻辑。 这里我们有一个函数sumOfTwoAsyncOperations ,它接受4个数字,并在operation1添加number1和number2在operation1添加number3和number4 。 我们可以看到,我们还创建了BlankOperation类型的“虚拟” operation3 BlankOperation ,其唯一目的是确认最后执行的操作,该operation3应为operation3并将消息打印到控制台中。 因此,如果现在调用此函数: 使用参数4和5,那么我们期望在控制台中获得以下输出: 运算1之和4 + 4 = 8 运算2总和5 + 5 = 10 操作2完成! 好吧..不! 我们得到这个: 操作2完成! 运算2总和5 + 5 = 10 运算1之和4 + 4 = 8 我们得到相反的顺序,但是即使那样也不能保证。 实际上,我们无法真正控制结果。 如果我们还记得本文中的讨论,则提到了依赖性管理,而其他一些操作并非完全免费或完全免费。 […]