我正在 Swift 中桥接同步/异步世界,并逐步采用异步/等待。我正在尝试调用一个从非异步函数返回值的异步函数。我知道显式使用Task是执行此操作的方法,例如,如此处所述。
该示例并不真正适合,因为该任务不返回值。
经过多次搜索,我无法找到任何我认为很常见的问题的描述:异步任务的同步调用(是的,我知道这可能会冻结主线程)。
理论上我想在同步函数中编写的是:
let x = Task {
return await someAsyncFunction()
}.result
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试这样做时,由于尝试访问而出现此编译器错误result:
'async' property access in a function that does not support concurrency
我发现的一种替代方案是这样的:
Task.init {
self.myResult = await someAsyncFunction()
}
Run Code Online (Sandbox Code Playgroud)
其中myResult必须归属为@State成员变量。
然而,这并没有按照我想要的方式工作,因为不能保证在Task.init()完成并进入下一个语句之前完成该任务。那么我怎样才能同步等待该任务完成呢?
我有一个演员:
actor StatesActor {
var job1sActive:Bool = false
...
}
Run Code Online (Sandbox Code Playgroud)
我有一个使用该演员的对象:
class MyObj {
let myStates = StatesActor()
func job1() async {
myStates.job1IsActive = true
}
}
Run Code Online (Sandbox Code Playgroud)
线:
myStates.job1IsActive = true
出现以下错误:
演员隔离属性“job1IsActive”无法从非隔离上下文中发生突变
如何使用 actor 正确存储/读取状态信息,以便 MyObj 可以使用它来读取和设置状态?
我有一个ObservableObject类和一个 SwiftUI 视图。当点击按钮时,我会在其中创建Task并调用(异步函数)。populate我以为这会populate在后台线程上执行,但整个 UI 都冻结了。这是我的代码:
class ViewModel: ObservableObject {
@Published var items = [String]()
func populate() async {
var items = [String]()
for i in 0 ..< 4_000_000 { /// this usually takes a couple seconds
items.append("\(i)")
}
self.items = items
}
}
struct ContentView: View {
@StateObject var model = ViewModel()
@State var rotation = CGFloat(0)
var body: some View {
Button {
Task {
await model.populate()
}
} label: { …Run Code Online (Sandbox Code Playgroud) 在 WWDC 2021 视频中,使用 Swift Actor 保护可变状态中,他们提供了以下代码片段:
\nactor ImageDownloader {\n private var cache: [URL: Image] = [:]\n\n func image(from url: URL) async throws -> Image? {\n if let cached = cache[url] {\n return cached\n }\n\n let image = try await downloadImage(from: url)\n\n cache[url] = cache[url, default: image]\n\n return cache[url]\n }\n\n func downloadImage(from url: URL) async throws -> Image { ... }\n}\nRun Code Online (Sandbox Code Playgroud)\n问题是 actor 提供可重入性,因此cache[url, default: image]引用有效地确保即使您由于某些竞争而执行了重复的请求,您至少在继续之后检查 actor\xe2\x80\x99s 缓存,确保您获得相同的图像对于重复的请求。
在那段视频中,他们说:
\n\n …
好的,我们都知道,在 Swift 的传统并发中,如果您在类中执行(例如)网络请求,并且在完成该请求时您引用了属于该类的函数,则必须传入[weak self],例如这:
func performRequest() {
apiClient.performRequest { [weak self] result in
self?.handleResult(result)
}
}
Run Code Online (Sandbox Code Playgroud)
这是为了阻止我们self在闭包中强烈捕获并导致不必要的保留/无意中引用已经从内存中删除的其他实体。
在异步/等待中怎么样?我在网上看到了相互矛盾的事情,所以我只想向社区发布两个示例,看看您对这两个示例有何看法:
class AsyncClass {
func function1() async {
let result = await performNetworkRequestAsync()
self.printSomething()
}
func function2() {
Task { [weak self] in
let result = await performNetworkRequestAsync()
self?.printSomething()
}
}
func function3() {
apiClient.performRequest { [weak self] result in
self?.printSomething()
}
}
func printSomething() {
print("Something")
}
}
Run Code Online (Sandbox Code Playgroud)
function3很简单 - 老式并发意味着使用[weak self].
function2我认为是对的,因为我们仍在闭包中捕获内容,所以我们应该使用[weak …
我正在尝试向 UIButton 添加异步 do/catch/defer 操作。但是,如果我只是调用 defer 块中的方法,则会收到Call to main actor-isolated instance method XXX in a synchronous nonisolated context错误。我发现的解决方法是将其包装在另一个Task块中,如下所示。只是想检查这是否是正确的方法?如果有人能解释该错误消息的实际含义,那就太好了。
@objc private func post(_ sender: UIButton) {
Task {
// defer { dismiss(animated: true) } -- Doesn't work
defer { Task { await dismiss(animated: true) } }
do {
try await doSomethingAsync()
} catch {
print(error)
}
}
}
Run Code Online (Sandbox Code Playgroud) 如何使用新的 Swift 5.5await关键字来等待一段时间?
DispatchQueue通常,对于完成处理程序,您可以通过使用's得到类似的结果asyncAfter(deadline:execute:):
func someLongTask(completion: @escaping (Int) -> Void) {
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
completion(Int.random(in: 1 ... 6))
}
}
someLongTask { diceRoll in
print(diceRoll)
}
Run Code Online (Sandbox Code Playgroud)
在 Swift 5.5 中如何将其转换为使用async& ?await
我试图检查演员的行为。这是Swift5.5提供的新功能。
我使用官方文档swift.org中的示例代码创建了一个游乐场:
import Foundation
actor TemperatureLogger {
let label: String
var measurements: [Int]
private(set) var max: Int
init(label: String, measurement: Int) {
self.label = label
self.measurements = [measurement]
self.max = measurement
}
}
let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max)
// Prints "25"
Run Code Online (Sandbox Code Playgroud)
但我的编译器在这个例子中失败了:
斯威夫特编译器错误:
不支持并发的函数中的“await”
Actor 隔离属性“max”只能从 Actor 内部引用
那么如何访问与参与者隔离的属性呢?
也许这是编译器或示例代码中的错误?
Xcode 版本 13.0 测试版 (13A5154h) Swift 版本 5.5
我正在尝试执行一系列网络请求,并希望限制新的 Swift 并发系统中的并发任务数量。对于操作队列,我们将使用maxConcurrentOperationCount. 在组合中,flatMap(maxPublishers:_:). 新的 Swift Concurrency 系统中的等价物是什么?
例如,它并不是非常相关,但请考虑:
func downloadAll() async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
for index in 0..<20 {
group.addTask { try await self.download(index) }
}
try await group.waitForAll()
}
}
Run Code Online (Sandbox Code Playgroud)
这会导致所有请求同时运行:
URLSession不尊重这一事实httpMaximumConnectionsPerHost很有趣,但这不是这里的突出问题。更一般地说,我正在寻找如何限制一系列并行运行的异步任务的并发程度。
我想要某种使用 同步的全局变量@MainActor。
这是一个示例结构:
@MainActor
struct Foo {}
Run Code Online (Sandbox Code Playgroud)
我想要一个像这样的全局变量:
let foo = Foo()
Run Code Online (Sandbox Code Playgroud)
但是,这不会编译并出现错误Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context。
很公平。我尝试在主线程上构建它,如下所示:
let foo = Foo()
Run Code Online (Sandbox Code Playgroud)
这可以编译!但是,它会崩溃EXC_BAD_INSTRUCTION,因为DispatchQueue.main.sync无法在主线程上运行。
我还尝试创建一个包装函数,例如:
let foo = DispatchQueue.main.sync {
Foo()
}
Run Code Online (Sandbox Code Playgroud)
并使用
let foo = syncMain {
Foo()
}
Run Code Online (Sandbox Code Playgroud)
但编译器无法识别if Thread.isMainThread并再次抛出相同的错误消息Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context。
这样做的正确方法是什么?我需要某种可以在应用程序启动之前初始化的全局变量。