可用数组模拟引用计数器:为每个对象分配唯一数字ID作为数组下标,增减引用时操作对应计数值,归零即回收资源;需手动配对调用retain/release,依赖协作规范防泄漏或误删。
可以用数组模拟引用计数器,核心是:每个被管理的对象对应一个计数项,增引用时+1,减引用时-1,归零即回收。关键不在数组本身,而在如何绑定对象生命周期与计数变化。
用数组存计数,用索引关联对象
不直接用对象作键(避免隐式转换和内存泄漏),而是分配唯一数字 ID,用该 ID 作为数组下标存储当前引用次数:
- 初始化一个空数组 refCounts = [],所有计数默认为 0(未注册)
- 每次创建需受管对象时,调用 alloc() 分配新 ID(如 id = refCounts.push(0) - 1)
- 对象内部或外部维护该 id,后续所有引用操作都基于它
手动增减引用并触发回收
引用关系由业务逻辑显式控制,不是自动追踪。例如函数传参、赋值、入容器时调用 retain(id);离开作用域、置 null、出容器时调用 release(id):
- retain(id):若 id 在范围内,refCounts[id]++
- release(id):若 refCounts[id] > 0,先 --refCounts[id],再检查是否为 0;是则执行清理(如 freeResource(obj)),并可选将 refCounts[id] = null 标记失效
- 注意:同一 id 被重复 release 要防护,避免负数或误删
实战:管理图像资源的简易池
假设加载一张图片后需在多个模块中复用,但不再需要时及时释放内存:
- 加载时:const imgId = alloc(); loadImage(url).then(img => { images[imgId] = img; retain(imgId); })
- 组件 A 使用:retain(imgId); render(imgId); // 组件卸载前 release(imgId)
- 组件 B 同样流程,两个组件共用同一 imgId
- 当最后一个 release(imgId) 将计数归零,就调用 images[imgId].src = '' 并 delete images[imgId],真正切断引用
注意事项与边界处理
这种手动方式轻量,但依赖协作规范:
- 必须成对调用 retain/release,漏掉会导致内存泄漏;多调一次可能导致提前释放
- ID 不要复用:即使某项已归零,也不应立即重用其索引,除非你实现“空闲链表”或定期压缩数组
- 不适用于闭包捕获、事件监听器等隐式强引用场景——这些需额外解绑逻辑,不能只靠计数
- 调试时可加 debugRef(id) 打印当前计数值和持有者列表(需额外维护持有者栈)

















