小编Lou*_*Lac的帖子

如何使用 Swift Concurrency 在后台执行 CPU 密集型任务而不阻塞 UI 更新?

我有一个ObservableObject可以完成 CPU 密集型繁重工作的程序:

import Foundation
import SwiftUI

@MainActor
final class Controller: ObservableObject {
    @Published private(set) var isComputing: Bool = false
    
    func compute() {
        if isComputing { return }
        
        Task {
            heavyWork()
        }
    }
    
    func heavyWork() {
        isComputing = true
        sleep(5)
        isComputing = false
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用 aTask在后台使用新的并发功能进行计算。这需要使用该@MainActor属性来确保所有 UI 更新(此处与该属性相关isComputing)都在主要参与者上执行。

然后,我有以下视图,其中显示一个计数器和一个启动计算的按钮:

struct ContentView: View {
    @StateObject private var controller: Controller
    @State private var counter: Int = 0
    
    init() {
        _controller = StateObject(wrappedValue: Controller()) …
Run Code Online (Sandbox Code Playgroud)

swift swiftui swift-concurrency

10
推荐指数
2
解决办法
1253
查看次数

当关联值是引用类型时,Swift 枚举大小

我阅读了有关 Swift 中枚举大小的文档,这是我的理解:

这个简单的仅包含一个“标签”来区分情况,默认情况下是一个UInt8值,即small = 0medium = 1等等。所以Size的大小为 1 个字节,可以用 来验证MemoryLayout<Size>.size。我还注意到,如果枚举的情况超过 255 个,显然标记大小会升级为 2 个字节。

enum Size {
    case small
    case medium
    case large
}
Run Code Online (Sandbox Code Playgroud)

第二种情况,如果枚举具有关联值,则其行为类似于联合。在这种情况下,枚举大小是标记的大小加上最大关联值的大小。在以下示例中,大小为 1 字节 + 16 字节(字符串),即 17 字节,也可以使用 进行验证MemoryLayout

enum Size {
    case small
    case medium
    case large
}
Run Code Online (Sandbox Code Playgroud)

最后一种情况,由于 Swift 是一种安全语言,使用标准非不安全 Swift 代码的引用始终有效,即始终指向内存中的值。T这允许编译器在是引用类型时优化此类枚举:

enum Opt<T> {
    case none
    case some(T)
}
Run Code Online (Sandbox Code Playgroud)

这里类型的实例T不能为nil(NULL),因此编译器在这种none情况下使用这个特殊值,因此当它是引用类型Opt时,其大小为 …

size enums memory-layout swift

8
推荐指数
1
解决办法
1076
查看次数

数组上的 Swift 减少比不安全的减少慢几个数量级

在我的程序中,我严重依赖以下我认为非常常见的模式。有一个保存数据的结构(可以说Double):

struct Container {
  var data: Double
}
Run Code Online (Sandbox Code Playgroud)

接下来,创建该结构的数组,可能很大:

let containers = (0...<10_000_000).map { _ in 
  Container(data: .random(in: -10...10))
}
Run Code Online (Sandbox Code Playgroud)

然后,对数组应用 (map)reduce 操作以提取值,例如:

let sum = containers.reduce(0.0) { $0 + $1.data }
Run Code Online (Sandbox Code Playgroud)

我最初认为这个特定的操作会很快,因为该操作足够简单,编译器可以优化边界检查和其他慢速操作:没有奇怪的索引计算,闭包中没有捕获,一切都是不可变的,等等......

但是,我将性能与这种不安全的 reduce 数组实现进行了比较:

extension Array {
  func unsafeReduce<Result>(
    _ initialResult: Result,
    _ nextPartialResult: (Result, Element) throws -> Result
  ) rethrows -> Result {
    try self.withUnsafeBufferPointer { buffer -> Result in
      try buffer.reduce(initialResult, nextPartialResult)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我用完全优化编译了两个程序,-O -unchecked我还使用了 Xcode 分析器来对程序进行计时,看来不安全的减少比安全的减少快 16 倍: …

optimization reduce swift

7
推荐指数
0
解决办法
338
查看次数

为什么Swift在for-in循环中使用下标语法比使用直接访问元素更快?

我读了着名的为什么处理排序数组比未排序数组更快?我决定玩游戏并尝试其他语言,如Swift.我对2个非常相似的代码片段之间的运行时间差异感到惊讶.

在Swift中,可以在for-in循环中以直接方式或使用下标访问数组中的元素.例如这段代码:

for i in 0..<size {
    sum += data[i]
}
Run Code Online (Sandbox Code Playgroud)

可写:

for element in data {
    sum += element
}
Run Code Online (Sandbox Code Playgroud)

size所述data长度和data可累加元件的阵列.

所以,我只是在Swift(代码吼叫)中实现了与我在第一段中提到的问题相同的算法,令我惊讶的是第一种方法比第二种方法快大约5倍.

我真的不知道后台下标的实现,但我认为直接访问Swift for-in循环中的元素只是语法糖.


我的问题是两种for-in语法之间有什么区别以及为什么使用下标更快?

这里是计时器的细节.我在2015年初的MacBook Air上使用Xcode 9.4.1和Swift 4.1以及Commande Line项目.

// Using Direct Element Access
Elapsed Time: 8.506288427
Sum: 1051901000
Run Code Online (Sandbox Code Playgroud)

VS

// Using Subscript
Elapsed Time: 1.483967902
Sum: 1070388000
Run Code Online (Sandbox Code Playgroud)

奖金问题:为什么Swift中的执行速度比C++慢100倍(两者都在Xcode项目的同一台Mac上执行)?例如,C++中的100,000次重复几乎与Swift中的1,000次重复相同.我的第一个猜测是,Swift是一种比C++更高级的语言,而Swift运行更多的安全检查.


这是我使用的Swift代码,我只修改了第二个嵌套循环:

import Foundation
import GameplayKit

let size = 32_768
var data = [Int]()
var sum  = 0 …
Run Code Online (Sandbox Code Playgroud)

optimization performance branch-prediction swift

5
推荐指数
1
解决办法
351
查看次数

Swift 5 Scanner 类很慢

我正在使用 SwiftScanner来解析文本文件中的字符串和数值。

由于卡特琳娜(10.15),可以使用scanDouble()scanInt()scanUpToCharacters(from:)scanUpTo(string:)方法。它们都返回 Optionals。

在 Catalina 之前,这些函数直接通过引用工作并返回指示操作是否成功的布尔值。函数签名就像:scanDouble(&result). 当时我自己实现了上面四个功能是这样的:

fileprivate extension Scanner {
    func scanUpToCharacters(from set: CharacterSet) -> String? {
        var result: NSString?
        return scanUpToCharacters(from: set, into: &result) ? (result as String?) : nil
    }

    func scanUpTo(_ string: String) -> String? {
        var result: NSString?
        return self.scanUpTo(string, into: &result) ? (result as String?) : nil
    }

    func scanDouble() -> Double? {
        var double = 0.0
        return scanDouble(&double) ? double …
Run Code Online (Sandbox Code Playgroud)

xcode swift

5
推荐指数
0
解决办法
440
查看次数

python while循环中的Continue语句

我是 python 的初学者,我在使用这段代码时遇到了问题:

count = 0

while count <15:
   if count == 5:
      continue
   print(count)
   count += 1
Run Code Online (Sandbox Code Playgroud)

当 count 的值 = 5 时,它会停止循环,就像有一个break语句一样。为什么会这样呢?请帮忙!

python continue break while-loop

0
推荐指数
1
解决办法
2352
查看次数