- 新增 RDEPUBReaderSearchCoordinator 与 RDEPUBSelectionState 管理搜索和选中状态 - 新增 BookmarkChromeStateTests、NavigationBackwardTests、SelectionAnnotateTests 等 UI 测试 - 新增多个边界测试 epub 样本(损坏结构、空归档、缺失文件、流式外链验证) - 重构阅读器 chrome 状态管理,统一 tool bar 与 search bar 交互 - 优化大书分页缓存策略(RDEPUBChapterSummaryDiskCache、RDEPUBPageCountCache) - 移除废弃的 RDEPUBLocationConverter 和 RDEPUBPageBreakPolicy - 更新 epub-bridge.js 与 JS bridge 通信协议 - 全面更新现有 UI 测试以适配新的 helper 和状态管理
78 lines
2.8 KiB
Swift
78 lines
2.8 KiB
Swift
import Foundation
|
||
|
||
// MARK: - 性能采样数据模型
|
||
|
||
/// 单个章节的性能采样数据,记录渲染和分页的耗时。
|
||
public struct RDEPUBTextPerformanceSample: Equatable {
|
||
/// 章节文件路径
|
||
public var chapterHref: String
|
||
/// HTML 渲染耗时(秒)
|
||
public var renderDuration: TimeInterval
|
||
/// CoreText 分页耗时(秒)
|
||
public var paginateDuration: TimeInterval
|
||
/// 分页后的页数
|
||
public var pageCount: Int
|
||
/// 富文本字符长度
|
||
public var attributedStringLength: Int
|
||
/// 是否命中分页缓存
|
||
public var cacheHit: Bool
|
||
|
||
public init(
|
||
chapterHref: String,
|
||
renderDuration: TimeInterval,
|
||
paginateDuration: TimeInterval,
|
||
pageCount: Int,
|
||
attributedStringLength: Int,
|
||
cacheHit: Bool
|
||
) {
|
||
self.chapterHref = chapterHref
|
||
self.renderDuration = renderDuration
|
||
self.paginateDuration = paginateDuration
|
||
self.pageCount = pageCount
|
||
self.attributedStringLength = attributedStringLength
|
||
self.cacheHit = cacheHit
|
||
}
|
||
}
|
||
|
||
// MARK: - 性能采样器
|
||
|
||
/// 书籍构建过程的性能采样器,用于监控每章的渲染和分页耗时。
|
||
///
|
||
/// 由 `RDEPUBTextBookBuilder` 在构建过程中使用,每章记录一个采样点,
|
||
/// 构建完成后输出汇总报告。
|
||
public final class RDEPUBTextPerformanceSampler {
|
||
/// 所有章节的采样数据列表
|
||
public private(set) var samples: [RDEPUBTextPerformanceSample] = []
|
||
/// 整本书构建的总耗时(秒)
|
||
public var totalBuildDuration: TimeInterval = 0
|
||
|
||
public init() {}
|
||
|
||
/// 记录单个章节的性能采样,并输出日志
|
||
public func record(_ sample: RDEPUBTextPerformanceSample) {
|
||
samples.append(sample)
|
||
#if DEBUG
|
||
print("[PERF] \(sample.chapterHref): render=\(formatMS(sample.renderDuration)) paginate=\(formatMS(sample.paginateDuration)) pages=\(sample.pageCount) cache=\(sample.cacheHit ? "HIT" : "MISS")")
|
||
#endif
|
||
}
|
||
|
||
/// 生成性能汇总报告,包含总渲染/分页耗时和缓存命中率
|
||
public func summary() -> String {
|
||
let totalRender = samples.reduce(0) { $0 + $1.renderDuration }
|
||
let totalPaginate = samples.reduce(0) { $0 + $1.paginateDuration }
|
||
let hitCount = samples.filter(\.cacheHit).count
|
||
return "[PERF] chapters=\(samples.count) render=\(formatMS(totalRender)) paginate=\(formatMS(totalPaginate)) total=\(formatMS(totalBuildDuration)) cacheHits=\(hitCount)/\(samples.count)"
|
||
}
|
||
|
||
/// 重置所有采样数据
|
||
public func reset() {
|
||
samples.removeAll()
|
||
totalBuildDuration = 0
|
||
}
|
||
|
||
/// 将秒转换为毫秒格式字符串
|
||
private func formatMS(_ duration: TimeInterval) -> String {
|
||
String(format: "%.0fms", duration * 1000)
|
||
}
|
||
}
|