require
简述
写lua其实最常用的就是require,所以去了解require也是必要的
步骤
首先require在表package.loaded中检查模块是否已经被加载。如果已经被加载则直接返回;反之则通过loadfile将其加载并保存在package.loaded中
实践
在unity中是如何启动lua的(Xlua)TODO写入到Xlua总结中去
注意
模块在任何情况下只能加载一次
垃圾回收
简述
Lua采用了自动内存管理,用来回收一些 不再被使用的对象(旁白:真的很简述)
弱引用表-收集lua语言中还可以被程序访问的对象
在一个没有被处理过的表中(强引用表),垃圾回收器不会去收集表中的对象。但是在弱引用表中,如果有一个key or value被回收了,那么对应的key value在表中都会被回收
使用__mode元方法实现弱表
a = {}
-- __mode 对应的k or v or kv, 就是将对应的引用设置为弱引用
mt = {__mode = "k"} --k or v or kv
setmetatable(a, mt)
析构器-允许收集不在垃圾收集器直接控制下的外部对象
collectgarbage-允许我们控制垃圾收集器的步长
Userdata数据类型
TODO
尾调用
调用栈
lua每次调用函数时,会将函数调用压入栈中,在函数调用结束时出栈
尾调用
在函数的结尾 return function 返回时调用函数就是尾调用
正确的尾调用
- 尾调用函数的返回值不能超过两个
- 不能在尾调用函数不能不能再发生一些运算操作:return function() + 1等
尾调用消除的意义
当发生正确的尾调用时,尾调用函数不会被调用栈压入栈中,而是直接使用 调用尾调用函数的入栈记录的数据(具体是什么需要看源码–这部分我后续看了源码会继续更新),这样尾调用函数就不会进行入栈操作。从而节约了性能
应用
递归–尾调用自身函数时,会发生栈溢出,但是如果是正确的尾调用则不会导致栈溢出
闭包与Upvalue
什么是闭包
当一个函数的内部函数调用了该函数内部定义的字段或者传参那么就会实例出一个闭包,该闭包未被释放前,他的上值不会被释放而是会被闭包所引用
什么是上值(Upvalue)
- 当函数对象实例了一个闭包时,那么该内部函数访问的外部对象就是上值,每个闭包都会有自己的upvalue。
- 如果多个闭包的上值对象是同一个引用类型(table),那么他们会共享该table。如果是值类型则每个闭包都拥有自己的上值
- 一个函数内部实例了多个闭包,他们会共享上值
闭包的应用
TODO
元方法与元表
- 一个表只能有一个元表,一个元表可以拥有多个元方法
- 使用元表和元方法可以实现面向对象编程
什么是元表
反过来解释或许更能理解:当你想让两个表之间发生一些特殊操作的话,就需要将两个表进行关联,在Lua中可以使用元表将两个表进行关联
什么是元方法
当表发生特定操作时,会去看有没有元表,如果有则看有没有特定操作的元方法
元方法函数实例
- __index:在table找不到对应的元素时调用
元方法对应表时:__index = table时会去该索引的table中查询,如果不存在则继续重复访问,直到下一个元表不存在__index
元方法对应函数时:则调用该函数返回函数的返回值给你查询的K,并table和key会作为参数传递给函数 - __newindex:当插入新的元素时调用
元方法对应函数时:
print("---- __newindex ----")
local tb1 = {a = "a"}
local tb1Meta = {}
--当__newindex元方法指向的是函数时,当发生赋值操作时,不会赋值而是会调用元方法
--p1 为表, p2 为Key,p3 为Value
setmetatable(tb1 ,{__newindex = function(p1, p2, p3)
print("新增元素", p1 == tb1, p2, p3)
end})
tb1.b = "1"
print(tb1.b)
- __tostring:目前只发现Print表时会调用__tostring元方法
print("---- __tostring ----")
local tb2 = setmetatable({},{__tostring = function()
return "__tostring"
end})
print(tb2)
Lua如何像C#一样实例一个对象
PS:这里存在一个疑惑点,有兴趣的同学可以用该代码去执行一下,你会发现我们使用New函数去实例化Lua对象时,指向的元表是同一个地址,但是在实例化出两个对象时,输出的元表位置是两个地址,这里需要去看一下Lua源码可能可以知道这个问题:后续我了解了会继续更新这个问题
简述
通过元表与元方法(__Index)来实现Lua的实例化
代码
Base.lua
Base = {}
Base.randonNum = math.random(1, 5)
--通过定义一个临时变量self,通过将该对象作为self的元表,并将该对象返回出
function Base:New()
local tb = {}
setmetatable(tb, {__index = Base})
print("Base表地址-", Base)
return tb
end
function Base:Func()
print("基类方法 Func", Base.randonNum)
end
return Base
TestBase.Lua
local baseClass = require("Base")
local temp1Base = baseClass:New()
local temp2Base = baseClass:New()
temp1Base:Func()
temp2Base:Func()
temp1Base.randonNum = 1
temp2Base.randonNum = 2
print(temp1Base.randonNum)
print(temp2Base.randonNum)
----测试元表初始化时是否会重新实例一个元表:结果元表会重新分配一个内存----
local meta1 = getmetatable(temp1Base)
local meta2 = getmetatable(temp2Base)
print(meta1)
print(meta2)
Lua中模仿C#的构造函数
简述
通过元表与元方法(__call)来实现 让函数一样的方式来调用表
代码
--使用 __call元方法
--1.可以让表像函数一样被调用,第一个参数为表本身,其余参数通过函数赋值
--2.使用该元方法可以模拟构造函数,以下为示例
local tb3 = {}
--构造函数ctor
function tb3:ctor(ptable)
self.age = ptable.age
self.name = ptable.name
print(self.age, self.name)
end
--元方法
setmetatable(tb3, {__call = function(p1, ...)
p1:ctor(...)
end})
--调用ctor函数
tb3({age = 10, name = "Alan"})
Lua中私有函数
简述
在lua程序设计这本书中的私有性-外部依旧还是能通过这个对象去调用这个对象内部的私有方法
方法
实现私有方法链接
通过元方法创建的lua对象,并在元表中剔除不需要外部调用的方法引用来实现私有函数-这样实例化出来的lua对象也不能调用自身的私有函数
local v = {};
v.x = 100;
v.y = 200;
function v.new()
local o = {};
setmetatable(o, v);
local mt = {f=v.f,x=v.x,y=v.y};
v.__index = mt;--metatable中只提供f方法,则f成为共有函数,g成为私有函数
return o;
end
function v:f()
return v.g(self);
end
function v:g()
return self.x + self.y;
end
return v;
Xlua优化TODO(单独出一篇为 对Xlua的认知)
C#与Lua通信的优化
在Lua缓存 CS.Unity等类对象