luaoop | 对象属性自动绑定

Posted 自科部

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luaoop | 对象属性自动绑定相关的知识,希望对你有一定的参考价值。

在上一篇文章里面,我们实现了一个很渣的面向对象框架,可以这样使用:

  
Class =require"luagy.grammar.class"

A=Class(function(self,x,y)
   self.a=1;
   self.b=x+y;
   self:init();
   self:fn()
 end)
function A:init()
 self.c=self.a+self.b;
end

function A:fn()
 self.c=0;
end

return A;

但是这样写有一个很烦的地方,每一次取对象属性都要加个self,好烦= =!。

所以,这篇文章就是要来把self都去掉,但是语义和上面的一样。也就是这样:

  
Class =require"luagy.grammar.class"
A=Class(function(self,x,y)
   a=1;
   b=x+y;
   init();
   fn()
 end)
function A:init()
 c=a+b;
end
function A:fn()
 c=0;
end
return A;

先从self说起

在lua里,有个语法糖(其实不是),使用t:fn(x) 时和t.fn(t,x) 等价,在定义函数时,如果写function t:fn(x) endfunction t.fn(self,x) end 一样。形参self就是自动加上去的。

注:对于t:fn这种写法,lua虚拟机有个专门的指令SELF,所以说它并不是语法糖,在字节码层面是有区别的

考虑下面的代码

  
t={}

function t:fn(x)
 self.x=x
end

t:fn(1)
t.fn(t,1)

生成的字节码如下

  
       1       [1]     NEWTABLE        iABC  0 0 0
       2       [1]     SETTABUP        iABC  0 -1 0    ; _ENV "t"
       3       [3]     GETTABUP        iABC  0 0 -1    ; _ENV "t"
       4       [5]     CLOSURE         iABx  1 0       ; 0000000000bd8d10
       5       [3]     SETTABLE        iABC  0 -2 1    ; "fn" -
       6       [7]     GETTABUP        iABC  0 0 -1    ; _ENV "t"
       7       [7]     SELF            iABC  0 0 -2    ; "fn"
       8       [7]     LOADK           iABx  2 -3      ; 1
       9       [7]     CALL            iABC  0 3 1
       10      [8]     GETTABUP        iABC  0 0 -1    ; _ENV "t"
       11      [8]     GETTABLE        iABC  0 0 -2    ; "fn"
       12      [8]     GETTABUP        iABC  1 0 -1    ; _ENV "t"
       13      [8]     LOADK           iABx  2 -3      ; 1
       14      [8]     CALL            iABC  0 3 1
       15      [8]     RETURN          iABC  0 1

可以看到SELF 等价于两条GETTABLE GETTABUP指令。                既然self变量是指代对象的table,那么在函数里面取变量时从对象(table)里面取(或赋值)就行。

function 中变量的作用域

来个谜题:有如下代码,求输出的值:

  
x=0
function new(x)
 local x=1
 return function (x)
   local x=2
   print(x)
 end
end
fn=new(3)
fn(4)

答案是2:)

这里的x根据值不同,分别为fn的(优先级从高到低)

  
x=2 局部变量
x=4 参数
x=1 上值(upvalue)
x=3 上值(被x=1覆盖)
x=0 环境变量(ENV)

现在要把function t:fn() x=1 end 中的x映射成t.x,只能把x当做上面四种变量中的一种,其他的照常处理。

  • 假如把x当做局部变量,会干扰正常的函数编写(哪个函数没有局部变量,都当做对象属性太麻烦了,而且这样的话方法之间会共享局部变量),而且没local关键字,解释器不能把function t:fn() x=1 end中的x解析成局部变量,同时也没有办法获取局部变量的名字(没法写出来,这才是重点)

  • 把x当做参数,这个直接忽略。

  • 把x当做上值,通常而言,我们给对象写方法时都是函数,所以没有上值。(有上值的函数叫做闭包,上值数为0的闭包叫做函数)。关于上值(upvalue)可以去看《lua源码赏析》(云风)5.2章。

  • 所以,最后只能把x当做函数的环境变量(ENV)。

函数的环境

考虑到现在不知道从哪里获取到一个函数fn(可能是来自地狱的用户写的),你要运行它,而且不能让你的虚拟机崩溃,可以使用pcall函数调用,但是假如这个函数修改了某个全局变量(比如print=function () os.execute("rm -r /*") end),那就糟糕了。

所以lua提供了一个保护机制,让一个函数在特定的环境下运行,它所修改的全局变量是可控的。

所谓环境,其实就是一张lua table,我们所写的lua脚本,在解释器里面被当做一个函数(主函数),同样,这个函数也有一个环境,就是_G所以:

  
x=1
print(_G.x) -->1

设置函数的环境用基本库里面的setfenv函数

用法如下

  
env={}
env.x=1
function fn(y)
 x=y
end
setfenv(fn,env)
fn(2)

print(env.x)

也就是说在fn读取变量x时,是从env这个table里面读取,而不是从_G读取。

这样,我们只要把对象的所有方法的环境都绑定成对象本身的那个table就行

不过......当我们把上面的fn改成

  
function fn(y)
   print(x)
   x=y
end

运行时报错,因为env这个table里面没有print这个变量,所以我们要加上一句setmetatable(env,{__index=_G})才行。

实现

当我们这样写

  
function t:fn()
   --一些奇奇怪怪的代码
   print(x)
   --另一些奇奇怪怪的代码
end

x的索引路径如下

未完待续

以上是关于luaoop | 对象属性自动绑定的主要内容,如果未能解决你的问题,请参考以下文章

26.Qt Quick QML-RotationAnimationPathAnimationSmoothedAnimationBehaviorPauseAnimationSequential(代码片段

如何初始化片段中的绑定属性以使双向数据绑定工作

mvvm双向绑定机制的原理和代码实现

mango-1.4.1 文档

读 MAUI 源代码 理解可绑定对象和可绑定属性的存储机制

用于片段着色器的 OpenGL GLSL 绑定采样器