From bd6e915fbd9b320f71fa344339776aaee2f1163c Mon Sep 17 00:00:00 2001 From: shenlei Date: Mon, 6 Jul 2026 11:56:22 +0900 Subject: [PATCH] =?UTF-8?q?=E9=82=BB=E6=8E=A5=E7=AA=97=E5=8F=A3=E6=B7=98?= =?UTF-8?q?=E6=B1=B0=E4=B8=8D=E5=86=8D=E6=B8=85=E9=99=A4=E5=89=8D=E7=9E=BB?= =?UTF-8?q?=E9=A2=84=E5=8F=96=E7=AB=A0=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scheduleAdjacentChapterPrefetches 的窗口外淘汰会把 maybePrefetchUpcomingChapters 刚预取的后续章节清掉,下一次 prepare 又触发重建,形成"淘汰-重建"循环。淘汰时保留当前章之后 lookahead 范围内的章节,lookahead 数量提为共享常量。 Co-Authored-By: Claude Fable 5 --- .../RDEPUBChapterWarmupOrchestrator.swift | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWarmupOrchestrator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWarmupOrchestrator.swift index ad7b265..7d23fab 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWarmupOrchestrator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBChapterWarmupOrchestrator.swift @@ -32,6 +32,8 @@ final class RDEPUBChapterWarmupOrchestrator { private let prepareRequestDebounceInterval: CFTimeInterval = 0.15 + private static let upcomingChapterLookaheadCount = 2 + init( context: RDEPUBReaderContext, store: RDEPUBChapterRuntimeStore, @@ -392,7 +394,10 @@ final class RDEPUBChapterWarmupOrchestrator { windowRadius: context.configuration.chapterWindowRadius ) - for evictable in store.evictableSpineIndices() { + // Keep chapters that maybePrefetchUpcomingChapters is responsible for, + // otherwise the two policies evict/rebuild the same chapter in a loop. + let retainedLookaheadIndices = upcomingLookaheadSpineIndices(after: spineIndex) + for evictable in store.evictableSpineIndices() where !retainedLookaheadIndices.contains(evictable) { store.evict(spineIndex: evictable) } @@ -410,11 +415,18 @@ final class RDEPUBChapterWarmupOrchestrator { } } + private func upcomingLookaheadSpineIndices(after spineIndex: Int) -> Set { + guard let publication = context.publication else { return [] } + let buildableIndices = buildableSpineIndices(in: publication) + guard let currentPosition = buildableIndices.firstIndex(of: spineIndex) else { return [] } + return Set(buildableIndices.dropFirst(currentPosition + 1).prefix(Self.upcomingChapterLookaheadCount)) + } + private func maybePrefetchUpcomingChapters( aroundAbsolutePageNumber pageNumber: Int, in bookPageMap: RDEPUBBookPageMap, threshold: Int = 3, - lookaheadChapterCount: Int = 2 + lookaheadChapterCount: Int = RDEPUBChapterWarmupOrchestrator.upcomingChapterLookaheadCount ) { guard let publication = context.publication else { return } let absolutePageIndex = pageNumber - 1