Lua lazy loader

Lua 文件懒加载实现

引言

自定义lua元表,实现代理、延迟载入的加载器(Ps.需要lua元表相关知识)

一个例子

local mkProxy = function(path)
	local instance = nil
	return setmetatable({}, {
		__index = function(t, k)
			if not instance then
				instance = require(path)
			end
			return instance[k]
		end,
		__newindex = function(t, k, v)
			if not instance then
				instance = require(path)
			end
			instance[k] = v
		end
	})
end

调用local proxy = mkProxy(path)返回一个设置了元表的空表 proxy,并不会真实reauire文件
当调用proxy的字段时,进入__index__newindex方法,检查instance,没有加载则require,然后返回调用的字段值
proxy的懒加载特性适合集中管理,在程序开始的地方,生成所有的代理,在实际用的地方加载真实数据,可解决程序同时加载大量文件卡顿的问题
__index, __newindex也可以添加高级功能,实现访问控制、统计、缓存等等

一点优化

多数情况下proxy都可以代替原表来使用,但proxy本质是个空表,pairsdumpdebug都是空的,对开发不友好
Programming in Lua第13章Tracking Table Accesses有这个问题的解决方法:private key指向原始table
private key是一个local table,外部拿不到
table作为键,外部也无法猜测也不会撞到真实键

下面是个改进后的mkProxy:

local index = {}

local checkIndex = function(t)
	local pIndex = rawget(t, index)
	if pIndex.__need_load__ then
		local chunk = require(pIndex.__path__)
		if type(rawget(chunk, "new")) == "function" then
			rawset(t, index, chunk.new())
		else
			rawset(t, index, chunk)
		end
	end
end

local mtIndex = function(t, k)
	checkIndex(t)
	return rawget(t, index)[k]
end

local mtNewIndex = function(t, k, v)
	checkIndex(t)
	rawget(t, index)[k] = v
end

local mtDelNewIndex = function(t, k, v)
	assert(false, "Say something")
end

local mt = {
	__index = mtIndex,
	__newindex = mtNewIndex,
}

local mtRead = {
	__index = mtIndex,
	__newindex = mtDelNewIndex,
}

local mkProxy = function(path, mt)
	return setmetatable({
		[index] = {
			__need_load__ = true,
			__path__ = path,
		}
	}, mt or mtRead)
end

一些思考

  • 安全
    private key一般方法确实无法访问,但能被pairs遍历到,就能用next函数拿到private key和原始table
    安全也是相对而言
  • 内存
    如果private key不指向原始table,而是新表,安全性是不是又提升了
    确实可以,而且实际开发中原始table可能有复杂的结构,读写可能有安全措施,新建一个数据友好型的新表,增加一个访问缓存,都可行,是高级用法,惟一的问题是新表占用内存,最初的proxy只是一个设置了元表的空表,加了新表、访问缓存却实打实的占用了内存,也可能随着访问次数逐渐增大

加载器的基本功能是延迟加载,完全访问原始表的功能
但是重载了__index__newindex配合private key实现高级功能的proxy则是个强大的工具
实际开发中跟据项目选用合适的功能,付出一些代价,换取开发便利,性能提升,高级功能,也是值得的。


© 2020. All rights reserved.

Powered by Hydejack v8.4.0