精华内容
下载资源
问答
  • 如果在module.lua定义一个全局变量 global_val = 5 那么在其它的文件中 require “module.lua” 后可以使用 global_val 这个全局变量,它的值就是5

    如果在module.lua 中定义了一个全局变量 global_val = 5
    那么在其它的文件中 require “module.lua” 后可以使用 global_val 这个全局变量,它的值就是5

    展开全文
  • Lua中的全局变量实现 全局变量在大多数语言中都属于“双刃剑”:全局变量可以表达程序中的全局概念,但是全局变量的使用会带来很多隐患。对于Lua这样的嵌入式语言,它是由宿主应用通过调用代码段(Chunk)来实现...

    之前,我们从C#代码的角度,为大家介绍了CPU耗时和堆内存的知识点,详见文末的知识点汇总。本期,我们将开启对本地资源检测中LuaCheck功能的解读,并结合简单的代码实例来讲解Lua检测中的具体情况,力图以浅显易懂的表达,让职场萌新或优化萌新能够深入理解。

    Lua中的全局变量实现

    全局变量在大多数语言中都属于“双刃剑”:全局变量可以表达程序中的全局概念,但是全局变量的使用会带来很多隐患。对于Lua这样的嵌入式语言,它是由宿主应用通过调用代码段(Chunk)来实现功能的。但“程序”概念并不明确,所以Lua语言为了解决这个问题,选择不使用全局变量,转而对全局变量进行模拟,把所有的全局变量保存在一个称为全局环境(global environment)的普通表中。

    Lua 5.2版本中修改了environment的概念,新增使用一个名为_ENV、以语法糖形式存在的预定义上值来模拟全局变量。例如一个代码段:

    local z=10
    x=y+z
    

    因为Lua语言把所有的代码段都当作匿名函数进行处理,实际上编译器会将代码段编译成如下形式:

    local _ENV = smoe value
    return function (...)
        local z = 10
        _ENV.x = _ENV.y + z
    end
    

    当_ENV的值是全局环境(global environment)表的时候,Lua便模拟出“x、y是全局变量”的情况。但实际上Lua语言是没有全局变量概念的,关于_ENV知识的详细解读可以阅读云风大神的Blog


    Lua中全局变量的危害

    1)在程序开发过程中,Lua语言中的全局变量不需要声明就可以使用,因此随着功能模块的增多,全局变量就会变得非常不稳定,稍有不慎便会重定义覆盖全局变量,产生各种不易查找的灾难性Bug。

    2)对于不是局部变量的访问,Lua会重定向到全局环境表_G。而局部变量始终具有优先访问权,因此如果一个全局变量和一个局部变量同名且同时存在,那么访问该变量时,得到的始终是局部变量的值。

    3)如果没有人为设置,Lua中通过模拟得到的全局变量,均会被_G表引用。但如果该全局变量引用了Unity中的Object对象,就会导致Unity中的Object对象被Destroy之后,_G表中的全局变量依然引用着该Object对象,引发泄露问题。更多关于Lua造成的堆内存泄露问题可以阅读本篇文章。

    4)开发过程中多个虚拟机同时操作一个变量时,会导致线程不安全。


    报告检测规则解读

    考虑到以上全局变量的使用隐患,因此我们在使用Lua语言开发时,应当尽量少用全局变量。

    UWA本地资源检测服务中,是否允许全局变量的下拉框中有四个选项:

    Not:表示不允许使用全局变量,任何地方出现的全局变量都将被视为不通过。

    Allow-Defined:表示允许在Main-Chunk中声明全局变量后,在函数体等地方使用。

    Main-Chunk指的是代码主流程块,例如下边的代码,foo变量位于Main-Chunk中,而qu变量在函数体中,不位于Main-Chunk中。

    foo = 4
    print(foo)
    
    function f()
       qu = 4
       print(qu)
    end
    

    Allow-Defined-Top:表示允许在任何地方声明全局变量。

    Add-Custom:选择该选项后,会弹出两个输入框(如下图),表示允许维护白名单,“自定义全局变量”表示白名单中的全局变量允许被声明、使用和修改;“自定义只读全局变量或字段”表示白名单中的全局变量及其字段允许被声明和使用,不允许被修改。这里的只读全局变量来源于用户自己的设定,可以维护一些不会被更改的数据表。

     


    1、设置一个未定义的全局变量

    本条规则下,检测的代码结果会和我们在UWA Scan Setting内的设置紧密相连。例如,在Main-Chunk中写如下代码:

    function f()
       baz = 5
    end
    

    选择Not模式,上述声明的全局变量(f、baz)会被视为不通过;

    选择Allow-Defined-Top模式,在非Main-Chunk的代码段中声明的全局变量(baz)会被视为不通过;

    选择Add-Custom模式,声明的全局变量(f、baz)如果不在白名单中,则会被视为不通过。


    2、更改一个未定义的全局变量

    例如,在项目中写如下代码:

    server.foo = "bar"
    

    在没有声明全局变量server的情况下,我们对全局变量server进行了更改,赋值了一个键值对。

    改正方式:在赋值之前进行声明:

    server={}
    server.foo = "bar"
    

    3、访问一个未定义的全局变量

    同样,如果在没有声明全局变量server的情况下,我们写了如下代码:

    server.sessions["hey"] = "you"
    

    那实际上是在没有进行声明的前提下,访问了全局变量server的“sessions”域。
    改正方式:在访问之前进行声明。


    4、试图设置一个只读的全局变量

    如果选择Add-Custom模式,那么在“自定义只读全局变量或字段”白名单中存在的全局变量理应保持为“只读”的属性。

    例如:设定全局变量string是只读全局变量,但出现如下代码:

    string = "foo"
    

    这就意味着应当保持“只读”属性的string,它的内容却被更改了。本条规则检测的就是这些发生变动的“只读”全局变量,开发团队需要去检查那些发生修改操作的代码,判断具体的使用情况是否符合初衷。


    5、试图设置一个全局变量的只读域

     

    function fun()
        bar.ff={}
    end
    

    这就导致只读全局变量bar的域ff被更改,在使用上和“只读”发生了冲突,需要进一步的排查。


    6、未被使用的隐式定义的全局变量

    此处指的是那些被定义了、但从未被使用过的全局变量。例如:声明了全局变量fun,但在之后的代码中从未使用过它:

    function fun()
       baz = 5
    end
    

    开发团队在经过必要的确认后即可删除这些多余的全局变量。


    7、试图设置一个全局变量的未定义域

    一种可能的情况如下:

    function table.clone()
        print('huh')
    end
    

    在没有声明table这个全局变量的情况下,我们给全局变量table赋值了一个表并添加了一个函数作为value。在这种情况下,开发团队就要检查相关全局变量和对应域的具体使用。


    8、试图访问一个全局变量的未定义域

    类似的:

    function table.clone()
        print('huh')
    end
    
    table.clone()
    

    类比上一条,在没有声明table这个全局变量的情况下,访问了table.clone()。在经过进一步的检查后,开发团队再决定是否进行声明的补充或者删除访问。


    希望以上这些知识点能在实际的开发过程中为大家带来帮助。需要说明的是,每一项检测规则的阈值都可以由开发团队依据自身项目的实际需求设置合适的阈值范围,这也是本地资源检测的一大特点。同时,也欢迎大家来使用UWA推出的本地资源检测服务,可帮助大家尽早对项目建立科学的检测规范。

     

    万行代码屹立不倒,全靠基础掌握得好!

    相关推荐
    《C#代码优化:斩断伸向堆内存的“黑手”》
    《C#代码优化:拯救你的CPU耗时》
    《场景检测:面片、光影和物理属性》
    《场景检测:Audio Listener、RigidBody和Prefab连接》
    《场景检测:雾效、Canvas和碰撞体》
    《特效优化2:效果与性能的博弈》
    《特效优化:发现绚丽背后的质朴》

    性能黑榜相关阅读

    《那些年给性能埋过的坑,你跳了吗?》
    《那些年给性能埋过的坑,你跳了吗?(第二弹)》
    《掌握了这些规则,你已经战胜了80%的对手!》

    展开全文
  • LUA教程环境声明全局变量 -60

    千次阅读 2020-07-16 09:53:41
    因为Lua所有的全局变量都保存在一个普通的表中,我们可以使用metatables来改变访问全局变量的行为。 setmetatable(_G, { __newindex = function (_, n) error("attempt to write to undeclared variable "..n, 2...

    全局变量不需要声明,虽然这对一些小程序来说很方便,但程序很大时,一个简单的拼写错误可能引起bug并且很难发现。然而,如果我们喜欢,我们可以改变这种行为。因为Lua所有的全局变量都保存在一个普通的表中,我们可以使用metatables来改变访问全局变量的行为。

    setmetatable(_G, {
        __newindex = function (_, n)
           error("attempt to write to undeclared variable "..n, 2)
        end,   
     
        __index = function (_, n)
           error("attempt to read undeclared variable "..n, 2)
        end,
    })

    这样一来,任何企图访问一个不存在的全局变量的操作都会引起错误:

    > a = 1
    stdin:1: attempt to write to undeclared variable a

    但是我们如何声明一个新的变量呢?使用rawset,可以绕过metamethod:

    function declare (name, initval)
        rawset(_G, name, initval or false)
    end

    or 带有 false 是为了保证新的全局变量不会为 nil。注意:你应该在安装访问控制以前(before installing the access control)定义这个函数,否则将得到错误信息:毕竟你是在企图创建一个新的全局声明。只要刚才那个函数在正确的地方,你就可以控制你的全局变量了:

    > a = 1
    stdin:1: attempt to write to undeclared variable a
    > declare "a"
    > a = 1       -- OK

    但是现在,为了测试一个变量是否存在,我们不能简单的比较他是否为nil。如果他是nil访问将抛出错误。所以,我们使用rawget绕过metamethod:

    if rawget(_G, var) == nil then
        -- 'var' is undeclared
        ...
    end

    改变控制允许全局变量可以为nil也不难,所有我们需要的是创建一个辅助表用来保存所有已经声明的变量的名字。不管什么时候metamethod被调用的时候,他会检查这张辅助表看变量是否已经存在。代码如下:

    local declaredNames = {}
    function declare (name, initval)
        rawset(_G, name, initval)
        declaredNames[name] = true
    end
     
    setmetatable(_G, {
        __newindex = function (t, n, v)
        if not declaredNames[n] then
           error("attempt to write to undeclared var. "..n, 2)
        else
     
           rawset(t, n, v)   -- do the actual set
        end
     
    end,
        __index = function (_, n)
        if not declaredNames[n] then
           error("attempt to read undeclared var. "..n, 2)
        else
           return nil
        end
    end,
    })

    两种实现方式,代价都很小可以忽略不计的。第一种解决方法:metamethods在平常操作中不会被调用。第二种解决方法:他们可能被调用,不过当且仅当访问一个值为nil的变量时。

    原文:LUA一个小巧脚本语言学习笔记

               LUA教程环境声明全局变量 -60

    展开全文
  • Lua脚本语言的变量是弱类型的,即...全局变量:顾名思义,其生命有效期是全局的,整个lua文件中都可以使用,可以在任意地方定义(函数参数除外),但有原则,使用时必须是先定义好的,否则就是nil,请看下面的代...

    Lua脚本语言的变量是弱类型的,即变量没有类型,值才有类型,同一名称的变量具体类型要看所赋值的类型,如下

    a=1    --整型
    a=1.0  --浮点型
    a="ab" --string型 
    a={}  --table型
    a=function() ... end --function型

    1、全局变量和局部变量

    全局变量:顾名思义,其生命有效期是全局的,整个lua文件中都可以使用,可以在任意地方定义(函数参数除外),但有个原则,使用时必须是先定义好的,否则就是nil,请看下面的代码

    print(i);
    function test(j)
       i = 1;
    end
    test(); --如果不执行test(), i未定义,都是nil
    print(i,j);

    执行结果是

    nil
    1   nil

    局部变量:只在某些特定的范围内有效的变量,称为局部变量,用local修饰。最主要的局部变量是定义在函数内部的局部变量。请看下面的代码片段

    local j=2; --虽然是定义的local变量,但却是在函数外部,
               --所以其作用域是它之后的整个文件,所以等同于全局变量
    function test()
       local i = 1; --局部变量,只在test函数内部有效
       print("j="..j);
    end
    test();
    print(i,j);

    执行结果为:

    j=2
    nil 2

    2、函数闭包

    函数闭包:闭包(closure),通过调用含有一个内部函数加上该外部函数持有的外部局部变量(upvalue)的外部函数(就是工厂)产生的一个实例函数
    闭包组成:外部函数+外部函数创建的upvalue+内部函数(闭包函数)
    这里,test是外部函数, local i是upvalue, function() i=i+1.. 是内部函数

    function test()
            local i=0   --局部变量
            return function() --尾调用,即尾部调用,将另一函数作为function类型返回
                i = i+1
                return i
            end
        end
        c1=test()
        c2=test(); --c1,c2是建立在同一函数,同一局部变量的不同实例上的两个不同的闭包
                --闭包中的upvalue各自独立,调用一次test()就会产生一个新的闭包
        print(c1()) -->1
        print(c1()) -->2//重复调用时每一个调用都会记住上一次调用后的值,就是说i=1了已经被记住了
        print(c2())    -->1//闭包不同所以upvalue不同    
        print(c2()) -->2

    函数嵌套:即函数内部定义了另一个函数,注意不是函数调用。因为lua将函数也看成是一种基本数据类型,可以用来做赋值和返回。实际也是秉承了c语言指针概念的基础上做的转换,将函数的地址作为基本数据类型(个人理解,不确定正确,待考证)。

    3、非局部变量

    非局部变量:即上面提到的upvalue,这个值改如何理解,如果你有C语言基础,我们不妨先将它理解为函数内部定义的“静态(static)变量”,但upvalue它有自己的特点。

    首先看upvalue的定义要求,一定是外部函数定义的的local局部变量,且在内部函数调用过。如下图中 j n k都是upvalue, 而h由于在内部函数中未调用,所以不是upvalue。(全局变量的作用域是全局的,所以谈论全局变量做upvalue无意义)

    function newCounter() --外部函数
        local j = 0; local n = 0;
        local k = 0; local h = 0;
        print("f1:",j,k,n);     --cd1
        return function() --内部函数
            print("f2:",j,k,n); --cd2
            j = n;
            k = n;
            n = n+1;
            return j,k,n;
        end
    end
    counter = newCounter(); --cd3
    print("f3:",counter()); --cd4
    print("----------分割线----------");
    print("f4:",counter()); --cd5

    执行结果

    f1: 0   0   0
    f2: 0   0   0
    f3: 0   0   1
    ----------分割线----------
    f2: 0   0   1
    f4: 1   1   2

    说明:cd3(code3)将函数赋值给counter,产生一个新实例,即生成一个闭包。从结果看cd4和cd5两次调用counter, 但外部函数的cd1只执行了一次,所以可以看出,闭包函数的外部函数部分只在第一次调用时执行,后面再次时调用直接跳到内部函数执行,所以upvalue的j k n的值只在第一次由外部函数初始化给定,后续的值仍然以某种形式“存活”下来,因此这个特性有点类似于c语言的静态变量。

    如果上面的嵌套函数再次赋值给counter1,则会生成一个新的闭包实例,它和之前的counter之间是相互独立的关系,我们看下面的代码

    function newCounter()
        local j = 0;local n = 0;local k = 0; local h = 0;
        print("f1:",j,k,n);
        return function()
            print("f2:",j,k,n);
            k = n;
            j = n;
            n = n+1;
            return j,k,n;
        end
    end
    
    counter = newCounter();
    print("f3:",counter());
    print("----------分割线----------");
    print("f4:",counter());
    
    print("**********分割线**********");
    
    counter1 = newCounter();
    print("f5:",counter1());
    print("----------分割线----------");
    print("f6:",counter1());

    执行结果:

    f1: 0   0   0
    f2: 0   0   0
    f3: 0   0   1
    ----------分割线----------
    f2: 0   0   1
    f4: 1   1   2
    **********分割线**********
    f1: 0   0   0
    f2: 0   0   0
    f5: 0   0   1
    ----------分割线----------
    f2: 0   0   1
    f6: 1   1   2

    说明: * * * * * * *分割线 * * * * * * *,上面是counter的执行结果,下面是counter1的,两次执行结果一样,证明counter和counter1之间是独立的。我们可以借助c++的概念来理解,将闭包函数看做是创建类(class)的实例,counter和counter1分别是相同类(class)的两个实例,之间没有关联关系,而upvalue则是对应的实例成员函数内部的静态变量。

    upvalue非局部变量可以使用debug.getupvalue和debug.setupvalue来获取和设置,具体函数说明如下:

    • getupvalue (f, up):此函数返回函数 f 的第 up 个上值的名字和值。 如果该函数没有那个上值,返回 nil 。 以 ‘(’ (开括号)打头的变量名表示没有名字的变量 (去除了调试信息的代码块)
    • setupvalue (f, up, value):这个函数将 value 设为函数 f 的第 up 个上值。 如果函数没有那个上值,返回 nil 否则,返回该上值的名字

    这里补充一点up的排序规律,我们看下面的代码

    function newCounter()
        local j = 0;local n = 0;local k = 0; local h = 0;
        local l=0;
        print("f1:",j,k,n);
        return function()
            print("f2:",j,k,n);
            l = n;
            h = n;
            k = n;
            j = n;
            n = n+1;
            return j,k,n;
        end
    end
    counter = newCounter();
    local i = 1;
    print("-----------------------");
    repeat
       name, val = debug.getupvalue(counter, i);
       if name then
           print("index", i, name, "=", val);
           if(name == "n") then
                debug.setupvalue(counter, 2, 10);
            end
            i = i + 1;
        end
    
    until not name
    
    print("-----------------------");
    print("f3:",counter());

    执行结果如下:

    f1: 0   0   0
    -----------------------
    index   1   j   =   0
    index   2   k   =   0
    index   3   n   =   0
    index   4   l   =   0
    index   5   h   =   0
    -----------------------
    f2: 0   10  0
    f3: 0   0   1

    从结果上看upvalue的顺序和内部函数return的顺序有关,return 前的排在前面(j k n),没有return的排在后面,且按照调用顺序排列(l h)。

    补充:闭包定义的两种写法:

    --写法1
    function f1(n)
        --local i=0;
       return function()
           print(n);
           return n;
        end
    end
    
    g1=f1(200);
    print(g1()); --如果写成g1(300),带入的参数会被忽略
    
    
    --写法2
    function f3(n)
        local i=0;
       function f4()
           print(n);
           return n;
        end
        return f4;
    end
    
    g2=f3(200);
    print(g2());
    展开全文
  • Lua语法命名规范 Lua语言对大小写是敏感的 标识符命名规则 Lua语言中的标识符(或名称)可以由:任意字母、数字、下划线组成,但是不能以数字开头 例如下面都是符合规则的命名 i i10 _ij ...
  • Lua中的非全局变量

    2021-04-30 17:00:10
    天入职游戏公司,主管让我学习lua,大部分时间都花在解决面向对象编程和安装lua与C交互的环境了 作为程序员没有自己的技术日志怎么行,今天就来吐槽一下lua学习时候采的坑 -- Meta class Shape = {area = 0} -- ...
  • lua中声明一个全局变量很简单 a=5 直接赋值就可以,而且也不会限制变量的类型,我们可以直接把number类型的变量赋值string a="Hello" 声明一个局部变量需要在前面加上关键字 local local b=10 局部变量在当前...
  • Unity中这样去获取这三个全局变量 using UnityEngine; using XLua; public class CShapeCallLua : MonoBehaviour { LuaEnv luaEnv; // Use this for initialization void Start () { luaEnv = new L
  • 运行效率:本地变量 * 4 ≈ 全局变量 * 3 ≈ 本地函数 * 2 ≈ 全局函数。测试数据:4/5/9/12 local a = 0 local b = 0 local function CheckFun() return false end rawset(_G, "CheckFunTest", CheckFun) ...
  • Lua中的 默认为全局变量,局部变量使用 local 在函数 if for 中定义的也是全局变量 在语句内部输出局部变量可以、但是在外部即无法输出 总结:带有 local 的全为 局部,not local 的即为全局
  • Lua全局变量

    2020-01-28 17:58:04
    全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。 >print(b) nil >b=10 >print(b) 10 > 如果你想删除一个全局变量,...
  • 所以这些局部的全局变量startX、posY、betweenX、betweenY等会和这类一样的生命周期,即这些局部全局变量值 startX、posY、betweenX、betweenY等会保存上次的运算结果。  明白原理之后,我通过下面的代码删除之前...
  • 04,Lua 全局变量

    2017-06-16 23:24:02
    Lua 全局变量
  • LUA中的全局变量环境

    千次阅读 2014-09-05 23:43:42
    LUA中的全局变量环境  ...LUA这货在全局变量的实现方式上用了一个绝对让我喜闻乐见的做法,复用已有的table机制,将全局变量都保存在_G表中。这样做好处很多。 从语言实现角度来说,不需要为了全局变量
  • Unity Lua 之 在 Unity 中 C#访问Lua全局变量、全局 table 和全局 function 的方法 目录 Unity Lua 之 在 Unity 中 C#访问Lua全局变量、全局 table 和全局 function 的方法 、简单介绍 二、C#访问Lua ...
  • Lua集锦二 全局变量_G

    2017-11-06 21:42:56
    Lua脚本层,Lua将所有的全局变量保存在一个常规的table中,这个table被称为全局环境,并且将这个table保存在一个全局变量_G中,也就是说在脚本中可以用_G获取这个全局table,并且有_G._G == _G,在默认情况,Lua在...
  • 1.全局变量的原形 在Lua中,要声明全局变量很简单,那就是定义变量的时候,前面不要加上local。 这个神秘的全局变量,其实本质上也是一个table,它把我们创建的全局变量都保存到一个... -- 定义一个全局变量 ...
  • Lua一个特性就是默认定义的变量都是全局的。为了避免这一点,我们需要在定义变量时使用 local 关键字。 但难免会出现遗忘的情况,这时候出现的一些 bug 是很难查找的。所以我们可以采取一点小技巧,改变创建全局...
  • lua全局变量

    2013-04-25 21:05:00
    全局变量不需要声明,虽然...因为Lua所有的全局变量都保存在一个普通的表中,我们可以使用metatables来改变访问全局变量的行为。 第一个方法如下: setmetatable(_G, { __newindex = function (_, n) ...
  • lua学习之全局变量

    千次阅读 2018-05-23 15:44:29
    全局变量保存在_G表中。全局变量不需要声明,对小的程序很方便。当程序很大时,可能会导致很难发现的bug.方法1:function declare(name, initval ) rawset(_G, name, initval or false) --rawset是会绕过metamethod...
  • Lua 全局变量

    2016-01-12 15:15:25
    Lua变量无需先声明(定义)才能使用,如果你要使用某个变量,直接使用就可以了。 未赋值的变量的值是nil。...print(b) --> 10如果你将一个变量手动赋值为nil,则会对该变量进行内存回收。b = nil print(b) --> nil
  • 我们知道lua脚本语言的变量是弱类型的,即变量没有类型,值才有类型,同一名称的变量具体类型要看所赋值的类型,如下 a=1 --整型 a=1.0 --浮点型 ...全局变量:顾名思义,其生命有效期是全局的,整个lua
  • 大体情况是这样的,我在主程序中设定的全局变量,在模块文件中可以访问到,并修改了这个全局变量的值,但是在模块调用返回后,再次使用这个全局变量的值,发现它没有被赋值,没有被改变。究其原因,发现是因为Lua的...
  • 全局变量与局部变量的使用 全局变量即使声明在函数方法里面,只要该方法被调用过,便可在方法外访问到该变量 局部变量的销毁是在语句块的结束 do a=10 local b=20 print(a,b) end print(a,b) >lua...
  • Lua脚本层,Lua将所有的全局变量保存在一个常规的table中,这个table被称为全局环境,并且将这个table保存在一个全局变量_G中,文章首先通过一个递归函数,打印出_G中所有的信息,然后说明了怎么使用全局变量_G,...
  • 像在函数中定义全局变量必须是调用函数之后才能使用的。 function F() local x = 1 y = 2 print(x) -- 1 print(y) -- 2 end F() print(x) -- nil print(y) -- 2 y = 3 print(y) -- 3 ...
  • 由于lua里面随手怎么的写少一个local啥的,就可以轻松创造出一个全局变量来,导致这种定义全局变量的问题挺容易发生的,尤其是在一个代码量比较大的项目里,一人一个,就挺稳的了。 那么为了尽量查找和解决这样的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,959
精华内容 3,983
关键字:

lua定义一个全局变量