|
| 1 | +local util = require 'share.utility' |
| 2 | + |
| 3 | +---@class gc |
| 4 | +---@field _list table |
| 5 | +local mt = {} |
| 6 | +mt.__index = mt |
| 7 | +mt.type = 'gc' |
| 8 | + |
| 9 | +mt._max = 10 |
| 10 | + |
| 11 | +local function destroyGCObject(obj) |
| 12 | + local tp = type(obj) |
| 13 | + if tp == 'function' then |
| 14 | + xpcall(obj, log.error) |
| 15 | + elseif tp == 'table' then |
| 16 | + local remove = obj.remove |
| 17 | + if type(remove) == 'function' then |
| 18 | + xpcall(remove, log.error, obj) |
| 19 | + end |
| 20 | + end |
| 21 | +end |
| 22 | + |
| 23 | +local function isRemoved(obj) |
| 24 | + local tp = type(obj) |
| 25 | + if tp == 'function' then |
| 26 | + for i = 1, 1000 do |
| 27 | + local n, v = debug.getupvalue(obj, i) |
| 28 | + if not n then |
| 29 | + log.warn('函数式析构器没有 removed 上值!', util.dump(debug.getinfo(obj))) |
| 30 | + break |
| 31 | + end |
| 32 | + if n == 'removed' then |
| 33 | + if v then |
| 34 | + return true |
| 35 | + end |
| 36 | + break |
| 37 | + end |
| 38 | + end |
| 39 | + elseif tp == 'table' then |
| 40 | + if obj._removed then |
| 41 | + return true |
| 42 | + end |
| 43 | + end |
| 44 | + return false |
| 45 | +end |
| 46 | + |
| 47 | +local function zip(self) |
| 48 | + local list = self._list |
| 49 | + local index = 1 |
| 50 | + for i = 1, #list do |
| 51 | + local obj = list[index] |
| 52 | + if not obj then |
| 53 | + break |
| 54 | + end |
| 55 | + if isRemoved(obj) then |
| 56 | + if index == #list then |
| 57 | + list[#list] = nil |
| 58 | + break |
| 59 | + end |
| 60 | + list[index] = list[#list] |
| 61 | + else |
| 62 | + index = index + 1 |
| 63 | + end |
| 64 | + end |
| 65 | + self._max = #list * 1.5 |
| 66 | + if self._max < 10 then |
| 67 | + self._max = 10 |
| 68 | + end |
| 69 | +end |
| 70 | + |
| 71 | +function mt:remove() |
| 72 | + if self._removed then |
| 73 | + return |
| 74 | + end |
| 75 | + self._removed = true |
| 76 | + local list = self._list |
| 77 | + for i = 1, #list do |
| 78 | + destroyGCObject(list[i]) |
| 79 | + end |
| 80 | +end |
| 81 | + |
| 82 | +--- 标记`obj`在buff移除时自动移除。如果`obj`是个`function`, |
| 83 | +--- 则直接调用;如果`obj`是个`table`,则调用内部的`remove`方法。 |
| 84 | +--- 其他情况不做处理 |
| 85 | +---@param obj any |
| 86 | +---@return any |
| 87 | +function mt:add(obj) |
| 88 | + if self._removed then |
| 89 | + destroyGCObject(obj) |
| 90 | + return nil |
| 91 | + end |
| 92 | + self._list[#self._list+1] = obj |
| 93 | + if #self._list > self._max then |
| 94 | + zip(self) |
| 95 | + end |
| 96 | + return obj |
| 97 | +end |
| 98 | + |
| 99 | +--- 创建一个gc容器,使用 `gc:add(obj)` 将析构器放入gc容器。 |
| 100 | +--- |
| 101 | +--- 当gc容器被销毁时,会调用内部的析构器(不保证调用顺序) |
| 102 | +--- |
| 103 | +--- 析构器必须是以下格式中的一种: |
| 104 | +--- 1. 一个对象,使用 `obj:remove()` 方法来析构,使用 `obj._removed` 属性来标记已被析构。 |
| 105 | +--- 2. 一个析构函数,使用上值 `removed` 来标记已被析构。 |
| 106 | +--- |
| 107 | +--- ```lua |
| 108 | +--- local gc = ac.gc() -- 创建gc容器 |
| 109 | +--- gc:add(obj1) -- 将obj1放入gc容器 |
| 110 | +--- gc:add(obj2) -- 将obj2放入gc容器 |
| 111 | +--- gc:remove() -- 移除gc容器,同时也会移除obj1与obj2 |
| 112 | +--- ``` |
| 113 | +return function () |
| 114 | + return setmetatable({ |
| 115 | + _list = {}, |
| 116 | + }, mt) |
| 117 | +end |
0 commit comments