Удаление метатаблиц из таблицы в Lua

Я хочу «отцепить» метатаблицу от таблицы, и мне было интересно, если:

tbl = setmetatable(tbl, false) -- or nil

как правильно это сделать? Я не смог найти никакой информации о том, как это правильно сделать. Нужно ли мне использовать оператор присваивания?

Кроме того, будет ли этого достаточно для уничтожения метатаблицы, прикрепленной к таблице, если метатаблица никогда не имела ссылки и была анонимной ?:

tbl = setmetatable({}, {__index = something})
-- later on:
tbl = nil

а сборщика мусора хватило бы, чтобы удалить обе таблицы?


person Mayron    schedule 09.09.2016    source источник
comment
nil должно быть нормально, false не принимает.   -  person Chris Tanner    schedule 09.09.2016
comment
lua.org/manual/5.3/manual.html, lua.org/cgi-bin/demo   -  person Piglet    schedule 09.09.2016


Ответы (1)


Согласно справочнику Lua, к которому вы всегда должны обращаться, прежде чем задавать здесь вопрос, setmetatable(tbl, nil) удалит метатаблицу таблицы tbl, если исходная метатаблица tbl не будет защищена. Или, лучше сказать, удаляет не метатаблицу, а ссылку на нее. Таблица, которая служила метатаблицей, конечно, не будет удалена, пока на нее есть другие ссылки.

Прежде чем вы спросите людей, работает ли простой вызов функции, попробуйте сами. Вы можете использовать https://www.lua.org/cgi-bin/demo или любой другой интерпретатор Lua, и вы получите ответ за секунды, не привлекая никого.

Запуск этого кода:

setmetatable({}, false)

or

setmetatable({})

приведет к

ввод: 1: неверный аргумент # 2 для 'setmetatable' (ожидается ноль или таблица)

Теперь вы знаете, что нельзя ввести false и нужно явно ввести nil.

Чтобы проверить эту __metatable вещь, которую вы бы прочитали в справочном руководстве, вы можете попробовать этот код

local tbl = setmetatable({}, {__metatable = true})
setmetatable(tbl, nil)

В результате получается следующий результат:

ввод: 2: невозможно изменить защищенную метатаблицу

Ко второй части вашего вопроса:

tbl = nil не будет удалять таблицу, на которую ссылается tbl. Он удалит только ссылку на него.

local a = {}
local b = a
b = nil
print(a)

а по-прежнему является таблицей. Вы удалили только одну из его ссылок.

Если ссылки не осталось, сборщик мусора может собрать таблицу.

setmetatable(tbl, {}) установит ссылку на таблицу, возвращаемую конструктором таблиц {}, и сохранит эту ссылку где-нибудь в недрах tbl.

Если tbl была последней ссылкой на эту таблицу, в какой-то момент она будет собрана как мусор. Тогда, конечно, единственная ссылка на таблицу, которую вы установили как метатаблицу, также исчезнет, ​​и она также будет удалена.

Если вы сделаете что-то подобное:

local a = {}
local b = setmetatable({}, a)

, a = nil не удаляет метатаблицу b

Так что да, он удалит обе таблицы, если не останется другой ссылки ни на одну из них.

person Piglet    schedule 09.09.2016
comment
Стоит отметить, что если разработчик действительно защитил метатаблицу, установив __metatable, вы все равно можете получить к ней , если debug.getmetatable существует (или если вы можете загрузить в библиотеку отладки самостоятельно) - в противном случае вы нужно было сделать это через C API - person Olipro; 09.09.2016