* 정의
__newindex는 테이블의 메타테이블 안에 정의할 수 있는 메타메소드이다. 테이블에서 테이블에 정의되어 있지 않은 새로운 키를 할당하려고 할 때 해당 테이블의 메타테이블에서 __newindex가 정의되어 있다면 새로운 키에 대한 처리 방식을 정의할 수 있다.
* 사용 이유
새로운 키가 할당되려고 할 때 동작방식을 정의함으로 써 여러 가지 기능을 만들어낼 수 있다. 예를 들어보면 읽기 전용 테이블을 구현할 수도 있다.
* 동작 방식
- 테이블에 새로운 키를 처리하는 과정
만약 테이블에 존재하지 않는 키에 대해 값을 할당하려고 하면 루아 인터프리터는 __newindex 메타메소드를 찾아보고 만약 __newindex가 정의되어 있다면 값을 할당하는 대신에 __newindex 메타메소드를 호출한다.
__newindex는 __index와 함께 사용하면 활용도가 더 높다. 읽기 전용 테이블, 기본 값을 가지고 있는 테이블, 상속 관련 기능들도 구현할 수 있다.
(메타메소드를 호출하지 않고 우회할 수 있는 rawset이라는 원시 함수를 제공한다. 이는 내부 로직을 구현할 때 무한 루프 방지 등 유용하게 사용될 수 있다.)
- __newindex 함수 버전 예시
__index와 다르게 새로운 값들을 할당하는 과정이기 때문에 value 매개변수도 같이 들어온다. 예시에서는 메타테이블에 정보를 저장하도록 만들었다.
local table1 = {}
local table2 = setmetatable({value1 = 1}, table1)
table1.__newindex = function(table, key, value)
local metatable = getmetatable(table)
--if not metatable then
-- -- 원래 테이블에 추가하도록 할수도 있다.
-- -- rawset(table, key, value) -- 무한 재귀를 막기 위해 메타메소드 우회
-- return
--end
-- metatable도 __newindex가 정의되어 있다면 이에 따라 수행된다.
-- 다 찾아보고 부모 클래스도 없으면 그냥 본인 테이블에 넣는 것으로 정했다.
-- 제일 아래에 있는 부모 클래스에 넣을 수도 있다.
local rv = metatable[key]
if not rv then
rawset(table, key, value)
else
metatable[key] = value
end
end
table2.newValue = 5
print(table2.newValue, table1.newValue)
table2.value1 = 10
print(table2.value1, table1.value1)
nil 5
10 nil
- __newindex 단순화 버전 예시
간단히 함수가 아닌 테이블로 지정하면 해당 테이블로 할당하게 된다.
local table1 = {}
local table2 = setmetatable({value1 = 1}, table1)
table1.__newindex = table1
table2.newValue = 5
print(table2.newValue, table1.newValue)
table2.value1 = 10
print(table2.value1, table1.value1)
nil 5
10 nil
- __newindex 함수 버전과 단순화 버전 호출 원리(추측)
myTable[key] = value / myTable.key = value로 할당할 때 다음과 같은 함수가 호출되어 처리될 것이라고 추측하고 있다. 메타메소드를 우회하기 위해 rawget, rawset을 사용하였다.
local function SetKeyValue(table, key, value)
-- 원래 있는 값이면 그냥 갱신해준다. 메타메소드를 우회하기 위해 rawget을 사용한다.
local targetValue = rawget(table, key)
if targetValue then
rawset(table, key, value)
return
end
-- 메타테이블에서 __newindex가 정의되어 있지 않다면 테이블에 그냥 넣어준다. 기본 동작이다.
local metatable = getmetatable(table)
if not metatable or not metatable.__newindex then
-- table[key] = value를 사용하면 다시 메타테이블의 __newindex 통하게 되기 때문에 무한 재귀가 발생한다.
-- table[key] = value
-- 따라서 메타메소드를 우회할 수 있는 rawset함수를 사용한다.
rawset(table, key, value)
return
end
-- 타입에 따라서 적절하게 할당해 준다.
local typeString = type(metatable.__newindex)
if typeString == "table" then
metatable.__newindex[key] = value
return
end
metatable.__newindex(table, key, value)
end
'Lua in Roblox' 카테고리의 다른 글
[Lua] 클래스 구현(접근 제어, 상속, 리플렉션, 캐스팅 등 지원) (0) | 2022.07.13 |
---|---|
[Lua] __index (0) | 2022.07.12 |
[Lua] metatable (0) | 2022.07.12 |
[Lua / Roblox] 미니 배틀 로얄 게임 #4 : 구조 정리, 개선된 점 (0) | 2022.07.11 |
[Lua / Roblox] 미니 배틀 로얄 게임 #3 : 맵 스폰 위치 결정 알고리즘 (0) | 2022.07.10 |