diff --git a/SwiftPamphletApp.xcodeproj/project.pbxproj b/SwiftPamphletApp.xcodeproj/project.pbxproj index d2d41ed1..01f7a70d 100644 --- a/SwiftPamphletApp.xcodeproj/project.pbxproj +++ b/SwiftPamphletApp.xcodeproj/project.pbxproj @@ -475,6 +475,9 @@ 08F14B422BBDA3EA005B46CC /* NukeVideo in Frameworks */ = {isa = PBXBuildFile; productRef = 08F14B412BBDA3EA005B46CC /* NukeVideo */; }; 08F14B442BBE2865005B46CC /* ViewComponentImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08F14B432BBE2865005B46CC /* ViewComponentImage.swift */; }; 08F51BC527A374A500693AB6 /* footer_js.html in Resources */ = {isa = PBXBuildFile; fileRef = 08F51BC427A374A500693AB6 /* footer_js.html */; }; + 3A05B7C92CE57A5C00B2B30F /* TaskCaseLoadFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A05B7C82CE57A5C00B2B30F /* TaskCaseLoadFile.swift */; }; + 3A05B7CB2CE57D6F00B2B30F /* TaskCaseSemaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A05B7CA2CE57D6F00B2B30F /* TaskCaseSemaphore.swift */; }; + 3A05B7CD2CE5829C00B2B30F /* TaskCaseJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A05B7CC2CE5829C00B2B30F /* TaskCaseJSON.swift */; }; 3A30EFD62CDA818B0029CB2F /* WeatherKit(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 3A30EFD52CDA818B0029CB2F /* WeatherKit(ap).md */; }; 3A3168932CE0FD53004DFC5C /* HomeiOSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3168922CE0FD53004DFC5C /* HomeiOSView.swift */; }; 3A3168952CE0FFCE004DFC5C /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3A3168942CE0FFCE004DFC5C /* Launch Screen.storyboard */; platformFilter = ios; }; @@ -974,6 +977,9 @@ 08F0F8C02C959B0600DC659B /* 开源-时间(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "开源-时间(ap).md"; sourceTree = ""; }; 08F14B432BBE2865005B46CC /* ViewComponentImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewComponentImage.swift; sourceTree = ""; }; 08F51BC427A374A500693AB6 /* footer_js.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = footer_js.html; sourceTree = ""; }; + 3A05B7C82CE57A5C00B2B30F /* TaskCaseLoadFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskCaseLoadFile.swift; sourceTree = ""; }; + 3A05B7CA2CE57D6F00B2B30F /* TaskCaseSemaphore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskCaseSemaphore.swift; sourceTree = ""; }; + 3A05B7CC2CE5829C00B2B30F /* TaskCaseJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskCaseJSON.swift; sourceTree = ""; }; 3A30EFD52CDA818B0029CB2F /* WeatherKit(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "WeatherKit(ap).md"; sourceTree = ""; }; 3A3168922CE0FD53004DFC5C /* HomeiOSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeiOSView.swift; sourceTree = ""; }; 3A3168942CE0FFCE004DFC5C /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; @@ -2309,6 +2315,17 @@ path = Category; sourceTree = ""; }; + 3A05B7C72CE57A4100B2B30F /* TaskCase */ = { + isa = PBXGroup; + children = ( + 3AE3F9522CE4532D007EFDFA /* TaskCase.swift */, + 3A05B7C82CE57A5C00B2B30F /* TaskCaseLoadFile.swift */, + 3A05B7CC2CE5829C00B2B30F /* TaskCaseJSON.swift */, + 3A05B7CA2CE57D6F00B2B30F /* TaskCaseSemaphore.swift */, + ); + path = TaskCase; + sourceTree = ""; + }; 3ADBA84D2CE209C000B0050B /* Performance */ = { isa = PBXGroup; children = ( @@ -2316,7 +2333,7 @@ 3ADBA8502CE216E900B0050B /* Perf.swift */, 3AA462352CE228CA00774B59 /* NotificationPreheat.swift */, 3AA462372CE261CC00774B59 /* TaskManager.swift */, - 3AE3F9522CE4532D007EFDFA /* TaskCase.swift */, + 3A05B7C72CE57A4100B2B30F /* TaskCase */, 3AE3F9602CE49FA2007EFDFA /* TaskCaseViews */, ); path = Performance; @@ -2921,6 +2938,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3A05B7C92CE57A5C00B2B30F /* TaskCaseLoadFile.swift in Sources */, 0871C61B2BA04D23000B620D /* CategoryRowView.swift in Sources */, 08F14B442BBE2865005B46CC /* ViewComponentImage.swift in Sources */, 08069CAB2BDE01E800D48E24 /* GuideListView.swift in Sources */, @@ -2932,6 +2950,7 @@ 3AF2A2E52BE231BD00F3BE1B /* StarInfosView.swift in Sources */, 3AF2A2E32BE2317300F3BE1B /* StarInfoListView.swift in Sources */, 3AE0D59A2BAB0A0600D6D925 /* DeveloperListView.swift in Sources */, + 3A05B7CD2CE5829C00B2B30F /* TaskCaseJSON.swift in Sources */, 3A3168932CE0FD53004DFC5C /* HomeiOSView.swift in Sources */, 084417772B99BA3F0049297D /* SidebarView.swift in Sources */, 3AA462362CE228CA00774B59 /* NotificationPreheat.swift in Sources */, @@ -2949,6 +2968,7 @@ 08A9E1A22BC25D0700A73764 /* ViewComponentMarkdown.swift in Sources */, 0887A59A2BA28F6D00131359 /* CSGuideView.swift in Sources */, 08D8EFE52BED825E00AA0020 /* BookmarkListView.swift in Sources */, + 3A05B7CB2CE57D6F00B2B30F /* TaskCaseSemaphore.swift in Sources */, 3AF2A2DF2BE22A8C00F3BE1B /* UnCategoryInfoListView.swift in Sources */, 3AE3F9622CE4A0C5007EFDFA /* TaskCaseUIUpdateView.swift in Sources */, 086A5F362744ED9600FECE02 /* RepoView.swift in Sources */, diff --git a/SwiftPamphletApp/App/SwiftPamphletAppApp.swift b/SwiftPamphletApp/App/SwiftPamphletAppApp.swift index 8b005c0f..7a4a68b4 100644 --- a/SwiftPamphletApp/App/SwiftPamphletAppApp.swift +++ b/SwiftPamphletApp/App/SwiftPamphletAppApp.swift @@ -49,20 +49,14 @@ struct SwiftPamphletAppApp: App { #elseif os(iOS) HomeiOSView() .onAppear { +#if DEBUG // background fetch BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.starming.fetch", using: nil) { task in self.handleAppRefresh(task: task as! BGAppRefreshTask) } scheduleAppRefresh() - // 任务示例 -// TaskCase().bad() - TaskCase().good() - // 任务管理器示例 -// taskgroupDemo() - - #if DEBUG - // 查看整体从进程创建到主界面加载完成时间,只在开发环境下执行 + // 查看整体从进程创建到主界面加载完成时间 if let processStartTime = Perf.getProcessRunningTime() { // 主界面加载完成,记录终点 let launchEndTime = DispatchTime.now() @@ -70,12 +64,22 @@ struct SwiftPamphletAppApp: App { // Pre-main print("Pre-main : \(String(format: "%.2f", (processStartTime - launchTime))) 秒") - // Post-main - print("进程创建到主界面显示时间: \(String(format: "%.2f", processStartTime)) 秒") } else { print("无法获取进程创建时间") } - #endif + + // 任务示例 +// TaskCase.bad() + TaskCase.good() + + // 任务管理器示例 +// taskgroupDemo() + + if let processStartTime = Perf.getProcessRunningTime() { + // Post-main + print("进程创建到进入主界面时间: \(String(format: "%.2f", processStartTime)) 秒") + } +#endif // 记录启动结束 os_signpost(.end, log: log, name: "Launch", signpostID: signpostID) @@ -102,8 +106,6 @@ struct SwiftPamphletAppApp: App { } - - #if os(iOS) // MARK: - Background Task func scheduleAppRefresh() { diff --git a/SwiftPamphletApp/HomeUI/HomeView.swift b/SwiftPamphletApp/HomeUI/HomeView.swift index 380ea104..f4eaab3e 100644 --- a/SwiftPamphletApp/HomeUI/HomeView.swift +++ b/SwiftPamphletApp/HomeUI/HomeView.swift @@ -82,6 +82,7 @@ struct HomeView: View { .onOpenURL(perform: { url in // 处理外部链接 }) + #endif } } diff --git a/SwiftPamphletApp/HomeUI/HomeiOSView.swift b/SwiftPamphletApp/HomeUI/HomeiOSView.swift index 7683fbe1..9770adeb 100644 --- a/SwiftPamphletApp/HomeUI/HomeiOSView.swift +++ b/SwiftPamphletApp/HomeUI/HomeiOSView.swift @@ -36,19 +36,19 @@ struct TaskCaseView: View { ScrollView { TaskCaseUIUpdateView(isBad: false) .onAppear { - Perf.showTime(des: "UI更新") + Perf.showTime("UI更新视图") } TaskCaseAnimationView(isBad: false) .onAppear { - Perf.showTime(des: "动画视图") + Perf.showTime("动画视图") } TaskCaseBigImageView(isBad: false) .onAppear { - Perf.showTime(des: "大图处理视图") + Perf.showTime("大图处理视图") } TaskCaseCacheView() // 异步执行,计算量大会影响主线程 -// TaskCasePriorityView(isBad: true) +// TaskCasePriorityView(isBad: false) // .onAppear { // Perf.showTime(des: "优先级视图") // } diff --git a/SwiftPamphletApp/Performance/Perf.swift b/SwiftPamphletApp/Performance/Perf.swift index 56cb6cfc..e33b3054 100644 --- a/SwiftPamphletApp/Performance/Perf.swift +++ b/SwiftPamphletApp/Performance/Perf.swift @@ -9,7 +9,7 @@ import Foundation // 性能工具 struct Perf { - static func showTime(des: String = "") { + static func showTime(_ des: String = "") { if let processStartTime = Perf.getProcessRunningTime() { print("进程创建到\(des)时间: \(String(format: "%.2f", processStartTime)) 秒") } @@ -33,6 +33,7 @@ struct Perf { let startTimeSec = kinfo.kp_proc.p_starttime.tv_sec let startTimeUsec = kinfo.kp_proc.p_starttime.tv_usec let startTime = TimeInterval(startTimeSec) + TimeInterval(startTimeUsec) / 1_000_000 + let currentTime = Date().timeIntervalSince1970 return currentTime - startTime } diff --git a/SwiftPamphletApp/Performance/TaskCase.swift b/SwiftPamphletApp/Performance/TaskCase.swift deleted file mode 100644 index 1b285353..00000000 --- a/SwiftPamphletApp/Performance/TaskCase.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// BadCase.swift -// SwiftPamphletApp -// -// Created by Ming on 2024/11/13. -// - -import Foundation - -struct TaskCase { - func bad() { - // 读取文件 - loadFileSynchronously() - - } - - func good() { - // 读取文件 - Task { - await loadFileAsynchronously() - } - } - - // MARK: - 读取文件 - // 同步读取方式 - 会阻塞主线程 - func loadFileSynchronously() { - // 模拟耗时操作,减少循环次数并添加延迟 - var content = "" - for i in 1...10 { - content += "这是第\(i)行内容\n" - Thread.sleep(forTimeInterval: 0.3) // 每次循环暂停0.3秒 - } - print("同步读取文件成功") - Perf.showTime(des: "异步读取文件") - } - - // 异步读取方式 - 推荐使用 - func loadFileAsynchronously() async { - do { - _ = try await withCheckedThrowingContinuation { continuation in - DispatchQueue.global().async { - var content = "" - for i in 1...10 { - content += "这是第\(i)行内容\n" - Thread.sleep(forTimeInterval: 0.3) // 每次循环暂停0.3秒 - } - continuation.resume(returning: content) - } - } - - // 更新UI要在主线程 - await MainActor.run { - print("异步读取文件成功") - Perf.showTime(des: "异步读取文件") - } - } catch { - await MainActor.run { - print("读取文件失败") - } - } - } -} diff --git a/SwiftPamphletApp/Performance/TaskCase/TaskCase.swift b/SwiftPamphletApp/Performance/TaskCase/TaskCase.swift new file mode 100644 index 00000000..87e0df28 --- /dev/null +++ b/SwiftPamphletApp/Performance/TaskCase/TaskCase.swift @@ -0,0 +1,23 @@ +// +// BadCase.swift +// SwiftPamphletApp +// +// Created by Ming on 2024/11/13. +// + +import Foundation + +struct TaskCase { + static func bad() { + TaskCase.badLoadFile() // 读取文件 + TaskCase.badSemaphore() // 信号量 + TaskCase.badJSONDecode() // JSON 解析 + } + + static func good() { + TaskCase.goodLoadFile() // 读取文件 + TaskCase.goodSemaphore() // 信号量 + TaskCase.goodJSONDecode() // JSON 解析 + } + +} diff --git a/SwiftPamphletApp/Performance/TaskCase/TaskCaseJSON.swift b/SwiftPamphletApp/Performance/TaskCase/TaskCaseJSON.swift new file mode 100644 index 00000000..0beeafdf --- /dev/null +++ b/SwiftPamphletApp/Performance/TaskCase/TaskCaseJSON.swift @@ -0,0 +1,58 @@ +// +// TaskCaseJSON.swift +// SwiftPamphletApp +// +// Created by Ming on 2024/11/14. +// + +import Foundation + +// 数据模型 +struct TCItem: Codable, Identifiable { + let id: Int + let title: String + let description: String +} + +extension TaskCase { + static func badJSONDecode() { + let jsonData = TaskCase.generateLargeJSON() + do { + _ = try JSONDecoder().decode([TCItem].self, from: jsonData) + + } catch { + print("解析失败: \(error)") + } + Perf.showTime("未优化JSON解析") + } + + static func goodJSONDecode() { + Task.detached(priority: .background) { + do { + _ = try await parseJSON() + Perf.showTime("异步优化JSON解析") + } catch { + print("解析失败: \(error)") + } + } + } + + // 异步解析JSON + @Sendable + static func parseJSON() async throws -> [TCItem] { + let jsonData = TaskCase.generateLargeJSON() + return try JSONDecoder().decode([TCItem].self, from: jsonData) + } + + static func generateLargeJSON() -> Data { + var items: [[String: Any]] = [] + for i in 0...10000 { + items.append([ + "id": i, + "title": "标题 \(i)", + "description": "这是一段很长的描述文本,用来模拟实际场景中的数据量 \(i)" + ]) + } + return try! JSONSerialization.data(withJSONObject: items) + } +} diff --git a/SwiftPamphletApp/Performance/TaskCase/TaskCaseLoadFile.swift b/SwiftPamphletApp/Performance/TaskCase/TaskCaseLoadFile.swift new file mode 100644 index 00000000..da59b700 --- /dev/null +++ b/SwiftPamphletApp/Performance/TaskCase/TaskCaseLoadFile.swift @@ -0,0 +1,48 @@ +// +// TaskCaseLoadFile.swift +// SwiftPamphletApp +// +// Created by Ming on 2024/11/14. +// + +import Foundation + +extension TaskCase { + // 同步读取方式 - 会阻塞主线程 + static func badLoadFile() { + // 模拟耗时操作,减少循环次数并添加延迟 + var content = "" + for i in 1...10 { + content += "这是第\(i)行内容\n" + Thread.sleep(forTimeInterval: 0.3) // 每次循环暂停0.3秒 + } + Perf.showTime("未优化文件读取") + } + + // 异步读取方式 - 推荐使用 + static func goodLoadFile() { + Task { + do { + _ = try await withCheckedThrowingContinuation { continuation in + DispatchQueue.global().async { + var content = "" + for i in 1...10 { + content += "这是第\(i)行内容\n" + Thread.sleep(forTimeInterval: 0.3) // 每次循环暂停0.3秒 + } + continuation.resume(returning: content) + } + } + + // 更新UI要在主线程 + await MainActor.run { + Perf.showTime("异步优化文件读取") + } + } catch { + await MainActor.run { + print("读取文件失败") + } + } + } + } +} diff --git a/SwiftPamphletApp/Performance/TaskCase/TaskCaseSemaphore.swift b/SwiftPamphletApp/Performance/TaskCase/TaskCaseSemaphore.swift new file mode 100644 index 00000000..2166018e --- /dev/null +++ b/SwiftPamphletApp/Performance/TaskCase/TaskCaseSemaphore.swift @@ -0,0 +1,37 @@ +// +// TaskCaseSemaphore.swift +// SwiftPamphletApp +// +// Created by Ming on 2024/11/14. +// + +import Foundation + +extension TaskCase { + static func badSemaphore() { + let semaphore = DispatchSemaphore(value: 0) + + DispatchQueue.global().async { + // 模拟耗时操作 + sleep(2) + semaphore.signal() + } + + // 等待信号量,会阻塞主线程 + semaphore.wait() + Perf.showTime("未优化信号量") + } + + static func goodSemaphore() { + Task { + await performAsyncTask() + Perf.showTime("异步优化信号量") + } + + // 异步任务函数 + @Sendable + func performAsyncTask() async { + try? await Task.sleep(for: .seconds(2)) + } + } +} diff --git a/SwiftPamphletApp/Performance/TaskCaseViews/TaskCasePriorityView.swift b/SwiftPamphletApp/Performance/TaskCaseViews/TaskCasePriorityView.swift index adafafda..a21af74a 100644 --- a/SwiftPamphletApp/Performance/TaskCaseViews/TaskCasePriorityView.swift +++ b/SwiftPamphletApp/Performance/TaskCaseViews/TaskCasePriorityView.swift @@ -29,10 +29,10 @@ final class CalculationPriorityViewModel: ObservableObject, @unchecked Sendable private var animationTimer: AnyCancellable? // 不当使用高优先级(会导致UI卡顿) - func runHighPriorityTasks() async { + func runHighPriorityTasks(iter: Int = 1) async { isCalculating = true let tasks = (1...10).map { id in - CalculationTask(id: id, iterations: 500000) + CalculationTask(id: id, iterations: iter) } // 使用高优先级运行所有任务 @@ -52,10 +52,10 @@ final class CalculationPriorityViewModel: ObservableObject, @unchecked Sendable } // 使用合适的优先级(UI流畅) - func runOptimizedTasks() async { + func runOptimizedTasks(iter: Int = 1) async { isCalculating = true let tasks = (1...10).map { id in - CalculationTask(id: id, iterations: 500000) + CalculationTask(id: id, iterations: iter) } // 使用较低优先级运行计算任务 @@ -156,12 +156,18 @@ struct TaskCasePriorityView: View { viewModel.startAnimation() if isBad == true { Task { - await viewModel.runHighPriorityTasks() + await viewModel.runHighPriorityTasks(iter: 500000) + Perf.showTime("高优先级执行完成") } } else { - Task { - await viewModel.runOptimizedTasks() + var tasks = [@Sendable () async -> Void]() + for _ in 0...10 { + tasks.append { + await viewModel.runOptimizedTasks(iter: 50000) + } } + performLowPriorityTasks(tasks: tasks) + Perf.showTime("低优先级执行完成") } } .onDisappear {