fix(swift): 清理并发与废弃 API 告警
This commit is contained in:
1 parent
a4b44a64fc
commit
f34ba88db9
6 files changed
+34
-12
No files matched your search
@@ -34,9 +34,9 @@ final class StatusPanel: NSPanel {
|
||||
defer: true)
|
||||
// 打开时按平铺布局测出的高度只是起点:真实内容会随数据刷新变高变矮,由滚动区域上报差值再校正
|
||||
makeContent = { [weak self] in
|
||||
NSHostingView(rootView: content().environment(\.reportPopoverOverflow) { overflow in
|
||||
NSHostingView(rootView: content().environment(\.reportPopoverOverflow, PopoverOverflowReporter { overflow in
|
||||
self?.adjustHeight(by: overflow)
|
||||
})
|
||||
}))
|
||||
}
|
||||
isFloatingPanel = true
|
||||
level = .statusBar
|
||||
|
||||
@@ -113,7 +113,7 @@ struct TimedLineChart: View {
|
||||
var grid = false
|
||||
var height: CGFloat = DS.Size.chartHeight
|
||||
|
||||
static let gapLimit: TimeInterval = 10
|
||||
nonisolated static let gapLimit: TimeInterval = 10
|
||||
|
||||
var body: some View {
|
||||
Canvas { context, size in
|
||||
@@ -143,7 +143,7 @@ struct TimedLineChart: View {
|
||||
}
|
||||
|
||||
/// 落在 [end − duration, end] 内的采样换算成坐标(0 在下、1 在上),按间隔断开成若干段
|
||||
static func segments(_ points: [TimedValue], duration: TimeInterval, end: Date, in size: CGSize) -> [[CGPoint]] {
|
||||
nonisolated static func segments(_ points: [TimedValue], duration: TimeInterval, end: Date, in size: CGSize) -> [[CGPoint]] {
|
||||
let start = end.addingTimeInterval(-duration)
|
||||
let inset = DS.Size.chartLine
|
||||
let usable = size.height - inset * 2
|
||||
@@ -584,6 +584,7 @@ struct ScoreBand: View {
|
||||
private func marker(width: CGFloat) -> some View {
|
||||
let color = DS.Grade.color(for: clamped)
|
||||
let grade = DS.Grade.bands.first { clamped < $0.upper }?.grade ?? "A+"
|
||||
let markerOffset = width * CGFloat(clamped) / 100
|
||||
return VStack(spacing: 0) {
|
||||
Text(verbatim: grade)
|
||||
.dsFont(.xs, weight: .semibold)
|
||||
@@ -594,7 +595,7 @@ struct ScoreBand: View {
|
||||
Triangle().fill(color).frame(width: DS.Space.s2, height: DS.Space.s1)
|
||||
}
|
||||
.fixedSize()
|
||||
.alignmentGuide(.leading) { dimensions in dimensions.width / 2 - width * CGFloat(clamped) / 100 }
|
||||
.alignmentGuide(.leading) { dimensions in dimensions.width / 2 - markerOffset }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,27 @@
|
||||
import Foundation
|
||||
import Localization
|
||||
import Metrics
|
||||
import SwiftUI
|
||||
|
||||
/// 用独立身份包装不可比较的闭包,避免 SwiftUI 把无关环境刷新都视为回调变化。
|
||||
struct PopoverOverflowReporter: Equatable, Sendable {
|
||||
private let id = UUID()
|
||||
private let action: @MainActor @Sendable (CGFloat) -> Void
|
||||
|
||||
init(_ action: @escaping @MainActor @Sendable (CGFloat) -> Void) {
|
||||
self.action = action
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func callAsFunction(_ overflow: CGFloat) {
|
||||
action(overflow)
|
||||
}
|
||||
|
||||
static func == (lhs: Self, rhs: Self) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
|
||||
/// ImageRenderer 无法渲染 ScrollView 等 AppKit 承载的控件,截图模式下改用平铺布局。
|
||||
extension EnvironmentValues {
|
||||
@Entry var isSnapshot = false
|
||||
@@ -10,7 +30,7 @@ extension EnvironmentValues {
|
||||
/// 菜单栏弹窗:区块去掉卡片底色,用细分隔线隔开,排得更紧凑
|
||||
@Entry var isPopover = false
|
||||
/// 菜单栏弹窗:滚动内容的实际高度比可视区域高出(负数为矮出)多少,交给面板调整窗口高度
|
||||
@Entry var reportPopoverOverflow: (@MainActor @Sendable (CGFloat) -> Void)? = nil
|
||||
@Entry var reportPopoverOverflow: PopoverOverflowReporter?
|
||||
}
|
||||
|
||||
// MARK: - 卡片
|
||||
|
||||
@@ -62,7 +62,7 @@ public final class EgressController {
|
||||
let environment = await Self.readEnvironment()
|
||||
self.environment = environment
|
||||
let hits = await EgressProber.measure(interfaceName: environment.physicalInterface) { done in
|
||||
Task { @MainActor [weak self] in self?.progress = done }
|
||||
Task { @MainActor in self.progress = done }
|
||||
}
|
||||
let analysis = EgressAnalysis.evaluate(hits, environment: environment)
|
||||
let geo = await lookUpGeo(hits: hits, analysis: analysis, interfaceName: environment.physicalInterface)
|
||||
|
||||
@@ -257,8 +257,9 @@ public final class ProcessExplainer {
|
||||
|
||||
nonisolated private static func executablePath(pid: Int32) -> String? {
|
||||
var buffer = [CChar](repeating: 0, count: 4 * Int(MAXPATHLEN))
|
||||
guard proc_pidpath(pid, &buffer, UInt32(buffer.count)) > 0 else { return nil }
|
||||
return String(cString: buffer)
|
||||
let length = proc_pidpath(pid, &buffer, UInt32(buffer.count))
|
||||
guard length > 0 else { return nil }
|
||||
return buffer.withUnsafeBytes { String(decoding: $0.prefix(Int(length)), as: UTF8.self) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,9 +90,9 @@ public final class SpeedTestController {
|
||||
liveBitsPerSecond = 0
|
||||
broadbandTask = Task { [limit] in
|
||||
let result = await BroadbandTest.run(limit: limit) { stage in
|
||||
Task { @MainActor [weak self] in self?.broadbandStage = stage; self?.liveBitsPerSecond = 0 }
|
||||
Task { @MainActor in self.broadbandStage = stage; self.liveBitsPerSecond = 0 }
|
||||
} progress: { speed, _ in
|
||||
Task { @MainActor [weak self] in self?.liveBitsPerSecond = speed }
|
||||
Task { @MainActor in self.liveBitsPerSecond = speed }
|
||||
}
|
||||
guard !Task.isCancelled else { return stopBroadband() }
|
||||
broadband = result
|
||||
@@ -217,7 +217,7 @@ public final class SpeedTestController {
|
||||
liveBitsPerSecond = 0
|
||||
downloadTask = Task { [limit] in
|
||||
let result = await ThroughputProbe.download(node.downloadURL, limit: limit) { speed, _ in
|
||||
Task { @MainActor [weak self] in self?.liveBitsPerSecond = speed }
|
||||
Task { @MainActor in self.liveBitsPerSecond = speed }
|
||||
}
|
||||
guard !Task.isCancelled else {
|
||||
downloadingNode = nil
|
||||
|
||||
Reference in new issue
Block a user