// 托管对象循环与等待帧回收:先记 ui.stats().objects,再调用 mountCycle,观察 phase 与 pending.value.status()。 // 关闭临时页仍可因业务引用保留等待任务;releaseCycle 清掉外部引用后,缓存不应让整个引用环常驻。 // 比较操作前后对象数时,关闭页面只解除展示根;holder/pending 外部引用也释放后才应回到基线。 holder = ui.state(0) pending = ui.state(0) phase = ui.state(0) // 通过 peer 指回包含自己的 Page,配合等待协程形成包含视觉缓存读依赖的引用环。 class CycleRect: RectBlock { peer: Object // sleep 归属该 rect,await 保留协程帧;先写 phase=1,只有实际恢复后才写 2 并改 Page 标题。 function wait(self: CycleRect): Unit { gate = ui.sleep(self,"sleep",300000) phase.set(1) done = await gate phase.set(2) self.peer.title = "等待已结束" } // 故意读取 peer.title,让布局依赖包含循环中的 Page;缓存应借用观察,不增加长期 GC 根。 function measure(self: CycleRect, mount: Mount, constraints: Constraints): MeasureResult { observed = self.peer.title return MeasureResult(180,40) } // 绘制也读取 peer.title,检查绘制缓存与等待帧不会共同意外保活循环。 function paint(self: CycleRect, mount: Mount, geometry: Bounds): DisplayList { observed = self.peer.title return RectBlock.paint(self,mount,geometry) } } // 创建临时 Page 和 CycleRect,相互引用后挂载,再把 Page/Task 分别放入两个外部 holder。 function mountCycle(): Unit { transient = Page() transient.title = "临时引用环" rect = CycleRect(180,40,8,"#3868D9",transient) transient.render(rect) holder.set(transient) phase.set(0) pending.set(ui.spawn(rect,"workflow",rect.wait)) } // 先关闭临时页,随后去掉两个外部引用;等待依赖无需等满五分钟,失去可达性后应整体回收。 function releaseCycle(): Unit { holder.value.close() holder.set(0) pending.set(0) } // 基线页面始终存在,用它比较操作前后的托管对象数,不把临时页面当作基线。 base = Page() base.title = "GC 基线" base.render(TextBlock("关闭临时 Page 后应回收引用环",2))