当前位置: 首页 > 知识库问答 >
问题:

Swift 3中的哪一次发送?

柯曦
2023-03-14

好的,所以我在Xcode 8中发现了新的快速调度API。我使用调度队列.main.async 很开心,我一直在 Xcode 中浏览调度模块以查找所有新的 API。

但是我也使用dispatch_once来确保像单例创建和一次性设置这样的事情不会被执行多次(即使在多线程环境中)…并且dispatch_once在新的调度模块中找不到?

static var token: dispatch_once_t = 0
func whatDoYouHear() {
    print("All of this has happened before, and all of it will happen again.")
    dispatch_once(&token) {
        print("Except this part.")
    }
}

共有3个答案

郑俊彦
2023-03-14

这里和互联网周围的其他答案都很好,但我觉得也应该提到这个小花絮:

dispatch_once的伟大之处在于它的优化程度,基本上在首次运行后以我几乎无法理解的方式否定了代码,但我有理由相信,这将比设置和检查(真正的)全局令牌快得多。

虽然令牌可以在Swift中合理实现,但必须声明另一个存储的布尔值并不是那么好。更不用说线程不安全了。正如文档所说,您应该使用“惰性初始化的全局”。是的,但是为什么要把全局范围弄得杂乱,对吗?

在有人说服我更好的方法之前,我倾向于在我将要使用它的范围内声明我的do-once闭包,或者合理地接近它,如下所示:

private lazy var foo: Void = {
    // Do this once
}()

基本上,我是在说“当我读到这篇文章时,foo应该是运行这个块的结果。它的行为方式与全局 let 常量完全相同,只是在正确的范围内。而且更漂亮。然后我会在我想要的任何地方称呼它,把它读成一些永远不会被使用的东西。我喜欢斯威夫特的_。这样:

_ = foo

这种非常酷的怪癖其实已经有一段时间了,但还没怎么见过爱情。它基本上在运行时将变量作为一个未调用的闭包保持不变,直到有东西想要看到它的< code>Void结果。在读取时,它调用闭包,丢弃闭包,并将结果保存在< code>foo中。< code>Void实际上不使用任何内存,因此后续读取(即< code>_ = foo)在CPU上不做任何事情。(不要引用我的话,有人请检查一下装配以确保正确!)想要多少有多少,Swift基本上第一次跑完就不再关心了!丢掉旧的< code>dispatch_once_t,让您的许多代码保持圣诞节第一次打开时的漂亮!

我的一个问题是,您可以在第一次读取之前将foo设置为其他内容,然后您的代码将永远不会被调用!因此,全局 let 常量,这阻止了这一点。问题是,类作用域中的常量不能很好地与 self 一起玩,所以不能与实例变量一起玩...但说真的,你什么时候把任何东西都设置为虚空??

这样,您需要将返回类型指定为< code>Void或< code>(),否则它仍然会抱怨< code>self。谁是唐克?

惰性只是使变量尽可能地惰性,因此Swift不会直接在init()上运行它。

相当时髦,只要你记得不要写信给它!:P

吕飞翼
2023-03-14

虽然“惰性var”模式允许我停止关心调度令牌,并且通常比dispatch_once()更方便,但我不喜欢调用站点的样子:

_ = doSomethingOnce

我希望这个语句看起来更像一个函数调用(因为它意味着动作),但它看起来一点也不像。此外,必须编写_=来显式丢弃结果很烦人。

有一个更好的方法:

lazy var doSomethingOnce: () -> Void = {
  print("executed once")
  return {}
}()

这使得以下内容成为可能:

doSomethingOnce()

这可能效率较低(因为它调用空闭包而不是仅仅丢弃Void),但提高清晰度对我来说是完全值得的。

王凯旋
2023-03-14

自 Swift 1.x 以来,Swift 一直在幕后使用dispatch_once来执行全局变量和静态属性的线程安全惰性初始化。

因此,上面的< code>static var已经在使用< code>dispatch_once,这使得它有点奇怪(并且再次将它用作另一个< code>dispatch_once的令牌可能会有问题)。事实上,如果没有这种递归,就没有安全的方法来使用< code>dispatch_once,所以他们放弃了它。取而代之的是,只使用建立在它之上的语言特性:

// global constant: SomeClass initializer gets called lazily, only on first use
let foo = SomeClass()

// global var, same thing happens here
// even though the "initializer" is an immediately invoked closure
var bar: SomeClass = {
    let b = SomeClass()
    b.someProperty = "whatever"
    b.doSomeStuff()
    return b
}()

// ditto for static properties in classes/structures/enums
class MyClass {
    static let singleton = MyClass()
    init() {
        print("foo")
    }
}

所以,如果您一直在使用dispatch_once进行一次性初始化并产生一些值,那就太好了——您可以将该值设为您正在初始化的全局变量或静态属性。

但是,如果您使用dispatch_once执行不一定有结果的工作,该怎么办?您仍然可以使用全局变量或静态属性执行此操作:只需使该变量的类型Void

let justAOneTimeThing: () = {
    print("Not coming back here.")
}()

如果访问全局变量或静态属性来执行一次性工作对您来说感觉不合适——比如说,您希望您的客户端在使用您的库之前调用一个“初始化我”函数——只需将该访问包在一个函数中:

func doTheOneTimeThing() {
    justAOneTimeThing
}

有关更多信息,请参阅迁移指南。

 类似资料:
  • 我得到以下错误 响应数据为空。我做错了什么,或者我在代码中遗漏了什么?

  • 为什么这在《雨燕3》中行不通?它会在运行时崩溃,并表示: “-[MY_APP_NAME.DisplayOtherappSCTRL TAP:]:无法识别的选择器已发送到实例0x17ECEB70”

  • 本文向大家介绍用过哪些二次开发的东西?相关面试题,主要包含被问及用过哪些二次开发的东西?时的应答技巧和注意事项,需要的朋友参考一下 Dedecms phpcms ecshop,基本这些的东西如果基础好了 学习起来都是没问题的。

  • 问题内容: 我试图将一个添加到UIButton,以便在点击时将触发一个功能。我正在使用,并出现一些错误: 由于未捕获的异常’NSInvalidArgumentException’而终止应用程序,原因:’-[[SwiftRunner.ViewController tapBlurButton]:无法识别的选择器已发送到实例0x149e07610’ 这大致就是我所拥有的: 问题答案: 从您的代码中,您正

  • 我想订阅一个主题后成功连接,但我不能这样做。我已经试过了EG.starscream(仅适用于WebSocket连接)、StompClient、ActionCableClient、FayeSwift等的所有库,但似乎都不起作用。我想连接并订阅Stomp客户端WebSocket,请给我推荐一些可以连接并订阅Stomp客户端的WebSocket库。

  • 我试图做简单的超文本传输协议后请求使用以下代码:Golang代码从另一个SO后 它发送了两次http请求(我尝试了向自己的web服务和Firebase消息服务器发送)。有人知道怎么回事吗?非常感谢。 编辑忽略我,找出是AVG反病毒造成的问题。如果我这样做:然后按enter键一次,AVG会中断,说它已经扫描了它,然后让它运行。这会导致http调用两次。如果我在运行前禁用了防病毒软件,那么http请求