refactor: 添加中文注释 + 优化模块结构
- 给全部 78 个 Swift 源文件添加详细的中文注释(文件级、类级、方法级) - 删除 LegacyRDReaderController/ 死代码目录(16 文件 4592 行) - 根目录翻页容器文件移入 ReaderView/ 目录 - Resources/ 移入 EPUBCore/Resources/(与使用者归属一致) - RDEPUBTextIndexTable.swift 移入 EPUBTextRendering/(消除反向依赖) - RDURLReaderController.swift 移入 EPUBUI/(入口控制器归入 UI 层) - 更新 podspec 资源路径
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
|
||||
// MARK: - Pagination Cache (WXRead-style: page ranges only, no attributedString)
|
||||
// MARK: - 分页缓存数据模型(WXRead 模式:只缓存页范围,不缓存富文本)
|
||||
|
||||
/// Per-chapter pagination metadata cached to disk.
|
||||
/// Mirrors WRChapterPageCount's caching: stores page NSRange + break reasons,
|
||||
/// NOT the full attributed string. Builder re-renders HTML on cache hit but
|
||||
/// skips the rd_paginatedFrames pagination step.
|
||||
/// 单个章节的分页元数据缓存,用于磁盘持久化。
|
||||
///
|
||||
/// 对标 WXRead 的 WRChapterPageCount 缓存策略:只存储每页的 NSRange + 分页原因,
|
||||
/// 不缓存完整的 NSAttributedString。缓存命中时重新渲染 HTML,但跳过 CoreText 分页步骤。
|
||||
public struct RDEPUBTextChapterPaginationCache: Equatable {
|
||||
/// 章节文件相对路径
|
||||
public var href: String
|
||||
/// 每页在章节富文本中的字符范围
|
||||
public var pageRanges: [NSRange]
|
||||
/// 每页的分页原因(语义边界、帧限制等)
|
||||
public var breakReasons: [RDEPUBTextPageBreakReason]
|
||||
public var semanticHints: [RDEPUBTextSemanticHint]
|
||||
|
||||
@@ -26,8 +29,10 @@ public struct RDEPUBTextChapterPaginationCache: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSCoding Archive (simple: strings + ints only)
|
||||
// MARK: - NSCoding 归档层(只使用字符串和整数类型)
|
||||
|
||||
/// 整本书的分页缓存归档,用于 NSKeyedArchiver 序列化。
|
||||
/// 包含所有章节的分页归档数据。
|
||||
final class PaginationCacheArchive: NSObject, NSSecureCoding {
|
||||
static var supportsSecureCoding: Bool { true }
|
||||
|
||||
@@ -47,14 +52,20 @@ final class PaginationCacheArchive: NSObject, NSSecureCoding {
|
||||
}
|
||||
}
|
||||
|
||||
/// 单个章节的分页归档,将 NSRange 拆分为 location/length 数组以便 NSSecureCoding 编码。
|
||||
final class ChapterPaginationArchive: NSObject, NSSecureCoding {
|
||||
static var supportsSecureCoding: Bool { true }
|
||||
let href: String
|
||||
/// 页范围的起始位置数组(与 rangeLengths 一一对应)
|
||||
let rangeLocations: [NSNumber]
|
||||
/// 页范围的长度数组
|
||||
let rangeLengths: [NSNumber]
|
||||
/// 分页原因的原始字符串数组
|
||||
let breakReasons: [String]
|
||||
/// 语义提示的原始字符串数组
|
||||
let semanticHints: [String]
|
||||
|
||||
/// 从缓存模型构建归档对象
|
||||
init(from cache: RDEPUBTextChapterPaginationCache) {
|
||||
self.href = cache.href
|
||||
self.rangeLocations = cache.pageRanges.map { NSNumber(value: $0.location) }
|
||||
@@ -86,6 +97,7 @@ final class ChapterPaginationArchive: NSObject, NSSecureCoding {
|
||||
self.semanticHints = semanticHints
|
||||
}
|
||||
|
||||
/// 将归档数据转换回缓存模型
|
||||
func toCache() -> RDEPUBTextChapterPaginationCache {
|
||||
let pageRanges = zip(rangeLocations, rangeLengths).map { loc, len in
|
||||
NSRange(location: loc.intValue, length: len.intValue)
|
||||
@@ -99,19 +111,27 @@ final class ChapterPaginationArchive: NSObject, NSSecureCoding {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RDEPUBTextBookCache
|
||||
// MARK: - 分页缓存管理器
|
||||
|
||||
/// Disk-persistent cache for per-chapter pagination metadata.
|
||||
/// Follows WXRead's WRChapterPageCount caching pattern:
|
||||
/// cache key = bookID + layout settings, cache value = page NSRange per chapter.
|
||||
/// Attributed strings are NOT cached — builder re-renders HTML on cache hit.
|
||||
/// 磁盘持久化的分页缓存层,对标 WXRead 的 WRChapterPageCount 缓存模式。
|
||||
///
|
||||
/// 缓存策略:
|
||||
/// - 缓存键 = SHA256(书籍ID + 字号 + 行距 + 内边距 + 页面尺寸 + schema版本)
|
||||
/// - 缓存值 = 每章的页 NSRange 列表 + 分页原因(NSKeyedArchiver 序列化)
|
||||
/// - 富文本不缓存:缓存命中时重新渲染 HTML,但跳过 CoreText 分页步骤
|
||||
/// - 线程安全:所有读写操作通过 serial DispatchQueue 串行执行
|
||||
public final class RDEPUBTextBookCache {
|
||||
|
||||
/// 缓存模式版本号,变更时旧缓存自动失效
|
||||
public var schemaVersion: Int = 1
|
||||
|
||||
/// 串行队列,保证缓存读写的线程安全
|
||||
private let queue = DispatchQueue(label: "com.rdreader.textbookcache", qos: .utility)
|
||||
/// 缓存文件目录
|
||||
private let cacheDirectory: URL
|
||||
|
||||
/// 初始化缓存管理器,自动创建缓存目录
|
||||
/// - Parameter subdirectory: Caches 目录下的子目录名
|
||||
public init(subdirectory: String = "RDEPUBTextBookCache") {
|
||||
let baseURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?
|
||||
.appendingPathComponent(subdirectory, isDirectory: true)
|
||||
@@ -120,10 +140,13 @@ public final class RDEPUBTextBookCache {
|
||||
try? FileManager.default.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)
|
||||
}
|
||||
|
||||
// MARK: - Cache Key
|
||||
// Mirrors WRChapterPageCount.currentCacheKeyWithBookId:
|
||||
// Encodes bookID + fontSize + lineHeightMultiple + contentInsets + pageSize
|
||||
// MARK: - 缓存键生成
|
||||
// 对标 WRChapterPageCount.currentCacheKeyWithBookId:
|
||||
// 编码 bookID + fontSize + lineHeightMultiple + contentInsets + pageSize
|
||||
|
||||
/// 生成缓存文件名(SHA256 哈希 + ".cache" 后缀)。
|
||||
///
|
||||
/// 任意布局参数变更都会导致缓存键不同,从而自动失效。
|
||||
public func cacheKey(
|
||||
bookID: String,
|
||||
fontSize: CGFloat,
|
||||
@@ -137,10 +160,10 @@ public final class RDEPUBTextBookCache {
|
||||
return hex + ".cache"
|
||||
}
|
||||
|
||||
// MARK: - Load / Save (pagination metadata only)
|
||||
// MARK: - 加载/保存(只缓存分页元数据)
|
||||
|
||||
/// Load cached pagination metadata for all chapters.
|
||||
/// Returns dictionary keyed by chapter href.
|
||||
/// 从磁盘加载缓存的分页元数据,返回以章节 href 为键的字典。
|
||||
/// 缓存未命中或反序列化失败时返回 nil。
|
||||
public func load(key: String) -> [String: RDEPUBTextChapterPaginationCache]? {
|
||||
queue.sync {
|
||||
let fileURL = cacheDirectory.appendingPathComponent(key)
|
||||
@@ -170,7 +193,7 @@ public final class RDEPUBTextBookCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// Save pagination metadata for all chapters.
|
||||
/// 将分页元数据保存到磁盘(原子写入,防止损坏)
|
||||
public func save(_ chapters: [RDEPUBTextChapterPaginationCache], key: String) {
|
||||
queue.sync {
|
||||
let fileURL = cacheDirectory.appendingPathComponent(key)
|
||||
@@ -186,8 +209,9 @@ public final class RDEPUBTextBookCache {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Invalidate
|
||||
// MARK: - 缓存失效
|
||||
|
||||
/// 清除所有缓存文件
|
||||
public func invalidateAll() {
|
||||
queue.sync {
|
||||
let fileManager = FileManager.default
|
||||
|
||||
Reference in New Issue
Block a user