lua(xlua)基础知识点记录二

1. 关于lua函数传参参数

在lua中给function传递参数的时候一般分为两种情况:值传递和引用传递

值传递:

值传递:数字、字符串、布尔值、nil等基本类型通过值传递。函数内部接收的是外部变量的副本,修改副本不会影响原始变量。

虽然我们都知道字符串是引用类型存储的数据,但是在函数传参的时候传递的是指向字符串内存地址的指针值​(即引用的副本),而非原始变量的引用本身

lua 复制代码
function Test(n)
	n = n + 1
end
local a = 1
Test(a)
print("输出a:"..a)
-- 结果a仍然是1

引用传递:

​表(table)和函数如果当作函数的参数传递时,传递的时指向具体变量的地址的副本,记住时副本!!可以理解成表本身就是一个指针,把这个指针当作参数传递给函数时,会重新生成一个指针,并且把该变量的指针地址值复制给新生成的指针。所以这种情况下,如果直接操作(增删)都可以正常影响指向的变量数据。但是如果时赋值操作,那么就会修改传递过去的副本指向的内容。

lua 复制代码
function Test1(t)
	t.a = 1
end

function Test2(t)
	t = {a = 15,b =3}
end

function Test3(t)
	t = nil
end

local t = {a = 10, b = 2}

Test1(t)
print("t.a:"..t.a.." t.b:"..t.b)
Test2(t)
print("t.a:"..t.a.." t.b:"..t.b)
Test3(t)
print("t.a:"..t.a.." t.b:"..t.b)

上述代码三次的输出都是: t.a:1 t.b:2,

.

调用Test1的时候直接修改了引用指向的内容,所以修改生效。此时外部的t.a = 1。

.

调用Test2的时候直接把引用副本指向了另一个新表的内存,那么这时候函数内部的t已经和外部传进去的t没有关系了,所以怎么修改都不会对外部的t造成影响。

.

调用Test3的时候和Test2一样,把引用副本指向了空,同样和外部的t没有关联关系了,所以外部t不受影响。

2. 关于c#和lua相互引用(xlua版)

userdata

我们都知道lua中有一种数据类型-userdata

.

userdata 是 Lua 为存储 C/C++ 等外部语言数据而设计的特殊类型,提供了一块原始内存区域,可直接存放结构体、指针、文件句柄等外部对象。它允许 Lua 通过指针或内存块直接操作外部环境的数据,是 Lua 与 C/C++ 交互的核心机制。例如,C 中定义的 struct People 可通过 userdata 传递给 Lua 脚本使用.

.

所以一个语言只要可以和c/c++交互,那么就可以通过userdata和lua交互,这也是c#和lua交互的底层原理。

.

xlua中的c#和lua交互

在xlua中,c#和lua的交互都是通过c这个他们两种语言都能到的媒介实现的。

大致流程如下:

lua call c#

lua调用c# -> 调用c -> 操作虚拟栈对参数压栈 -> 调用c#wrap函数/反射查找函数 ->

把lua函数参数转换成c#类型 -> 完成调用

c# call lua

c#调用lua -> c#调用c函数 -> 通过capi找到lua函数(lua_getglobal) -> c#数据转成lua数据(lua_pushinteger) ->

通过capi调用lua函数(lua_pcall)

xlua中c#和lua相互引用

在xlua框架中,可以很方便的在lua中持有C#的对象变量并使用,反之一样。

lua持有c#对象:

通过上述的userdata了解,我们可以大致了解到,其实c#的变量在lua中是以userdata类型的数据存储的。

事实上在lua中从来没有真正的持有c#的真正变量。

​C#对象在Lua中的表示

在xLua中,C#对象传递到Lua时会被封装为userdata类型。这个userdata并不直接存储C#对象本身,而是存储一个唯一标识符(objId)​,用于关联C#端的实际对象

引用关系的管理(lua持有#)

但是我们也知道c#的gc是并不清楚lua环境中该userdata的引用情况的,如果直接不管lua中是否引用只要c#段就没有引用就GC掉显然不符合我们的需求。所以xlua中设计了ObjectTranslator.cs。

ObjectTranslator通过ObjectPool和ReverseMap维护对象与objId的映射,确保C#对象不被GC回收(只要Lua仍持有userdata)

而在lua中,userdata是被存放在弱引用表中的,只要lua中没有强引用该对象的存在,那么该userdata就可以被lua的gc回收掉,防止lua一致持有着objid而c#无法回收对应对象。

lua对象在c#中的表示:

所有Lua对象在C#中通过LuaBase基类管理,其构造函数调用luaL_ref在Lua注册表中创建引用,析构时调用luaL_unref释放

其实讲了那么多,就是为了理解如下代码:

csharp 复制代码
public class Main{
    public class TestClass
    {
    }

    public static TestClass MyClassTest;

    public static TestClass CreateTestClass()
    {
        if (MyClassTest == null) MyClassTest = new TestClass();
        return MyClassTest;
    }

    public static void DestroyTestClass()
    {
        MyClassTest = null;
    }
}
lua 复制代码
local myClass,myClass2,myClass3

local function Execute()
    myClass = CS.Main.CreateTestClass()

    myClass2 = CS.Main.MyClassTest
    myClass3 = myClass2
    myClass2 = nil

    collectgarbage("collect")
    CS.System.GC.Collect()

    CS.Main.DestroyTestClass()

    collectgarbage("collect")
    CS.System.GC.Collect()
end

pcall(Execute)

由上面讲述我们可以知道,一个c#变量在lua中的话,两端是各自持有各自的类型(userdata和TestClass),上述lua代码中的myClass,myClass2和myClass3就是对应的userdata。

.

即使myClass2 = nil后,myClass3和myClass仍然持有该userdata,所以第一次执行双方gc,只会回收掉myClass2。myClass3和myClass以及c#段的MyClassTest都没收影响。

.

那么再来看CS.Main.DestroyMyClass() 这句之后c#侧的MyClassTest引用被置空,注意只是引用实际变量还被lua持有,所以即使执行了C#的gc,真正的对象仍不会被回收掉,因为myClass 和myClass3仍然还存在着并且持有着。

.

那么这时候如何回收掉TestClass对应的数据,只需要再加上
myClass2 = nil
myClass = nil
collectgarbage("collect")
CS.System.GC.Collect()

这样的话lua测得强引用也没了,都可以被回收了

相关推荐
我是唐青枫2 小时前
C#.NET 泛型详解
开发语言·c#·.net
Yasin Chen2 小时前
C# StringBuilder源码分析
开发语言·c#
格林威3 小时前
Baumer工业相机堡盟工业相机如何通过YoloV8模型实现人物识别(C#)
开发语言·人工智能·数码相机·yolo·计算机视觉·c#
Rabbb4 小时前
C# JSON 反序列化时,忽略转换失败的属性 JTokenSafeToExtensions
后端·c#·json
三目条件4 小时前
C#将类属性保存到Ini文件方法(利用拓展方法,反射方式获取到分组名和属性名称属性值)
java·开发语言·c#
柠檬味的薄荷心6 小时前
【C#补全计划:枚举】
开发语言·c#
白雪公主的后妈7 小时前
C#——循环(while循环和do-while循环)
c#·continue·while·break·dowhile
唐青枫7 小时前
C#.NET EFCore 详解
c#·.net
The Mr.Nobody9 小时前
高性能上位机界面设计范式:C#与C++/C开发调试无缝衔接
c语言·c++·c#