找回密码
 点一下
查看: 1512|回复: 13

问一个排泄问题

[复制链接]
发表于 2009-3-14 21:53:56 | 显示全部楼层 |阅读模式
比如这里有个函数
[jass]
function createu takes player p,location l returns nothing
call PolledWait(10)
call CreateUnitAtLoc(p,'hfoo',l,bj_UNIT_FACING)
endfunction

function callu takes nothing returns nothing
local unit u=GetTriggerUnit()
local player p=GetOwningPlayer(u)
local location l=GetUnitLoc(u)
call createu(p,l)
set u=null
endfunction
[/jass]
这样,玩家p和点l没有被清除,就产生了泄露
要排泄只需加上
[jass]call RemoveLocation(l)
set l=null
set p=null[/jass]
这里就有疑问了...
因为p和l是被当作参数传递给上面的函数来使用的,如果接着set u=null的下面排泄掉了,那上面的函数不就没有东西可用了么...
(这里为了方便理解,特意在创建步兵前加了10秒等待)
因为据偶观察,一个函数在执行时,不管上一句调用的函数里有多少东西没做完,这个函数会继续往下执行
所以猜想,如果接着排泄完了,那么上一个函数等待完后想执行创建单位,会不会有以下几种可能...
1 参数已经被传递过来了,并且在内存里占了不同的位置(即h2i值是独立的),所以不管之前有没排泄都会正确创建步兵,这时就需要在创建步兵后再次排泄
2 参数已经被传递过来了,但h2i值与传递前的一样,如果在前面就被清空,那么这里也会变null
3 参数在传递过来之后就被清空了,但因作为参数,所以会在内存里出现一块临时地址用于储存,等到动作执行完后会自动清空
4 当call createu(p,l)时,首先会把参数的值赋予该函数下每个动作后才会继续执行下面的set u=null等等动作,因为已被赋值,所以动作能顺利完成,当函数执行完毕后,自动清空

好吧...简单的说,就是清空点l和玩家p应该在哪清...

可能是以前看教程时把当时觉得难以理解的东西跳过了,导致出现了一些基础理论不足...现在回头去找也没见到类似的问题
在看别人的演示时,基本没有发现传递参数后排泄的过程,又没见过象样的讨论帖...于是此疑问便诞生...
发表于 2009-3-14 22:12:38 | 显示全部楼层
用完的时候清除就可以了。。。

参数的传递使用的概念其实很简单。
传参数的时候,只是传递一个该物品具体存放的地址。
而并不是实际的这个东西。

如:您把物品存在一排储物柜的某一个柜子里。每个柜子都有一个编号。
当您要把这件东西给别人时,只需要告诉别人您放在哪个柜子里,别人就可以拿到这个东西(假设柜子没有锁,别人也不会去拿拿走您的东西)。

所谓的“排泄“其实只是把不用的东西扔掉,不占用柜子的空间,可以给其它人使用而已。
如果您在告诉别人柜子编号之前,已经把这个物品丢掉了,那么别人自然无法在这个柜子里找到这件东西。
所以,您只能是在告诉别人东西放在哪里,别人去找到并使用完之后。才考虑把这件东西扔掉。
回复

使用道具 举报

 楼主| 发表于 2009-3-14 22:15:12 | 显示全部楼层
所以想知道什么时候才算作"用完"
是在传递后就被使用完了呢,还是必须等函数内所有动作执行完一直到endfunction才叫用完

所以才有了这个问题...
究竟是该在callu里就排泄完呢,还是该在createu里排泄,或是两个都要排泄

如果一直到endfunction才叫用完,那如果createu有一个小时的循环呢...岂不是在callu里就排泄不能了
回复

使用道具 举报

发表于 2009-3-14 22:20:23 | 显示全部楼层
用完其实很好理解。
即是:您确定永久不再使用这个东西的时候。

像您举的这个例子。当这个l和p不再需要的时候。

call CreateUnitAtLoc(p,'hfoo',l,bj_UNIT_FACING)

call createu(p,l)
之后什么位置清除都无所谓。只要您确定它不再需要被使用到即可。
不论是在哪个函数中清除,或者是在某段语句之中的地方,都是可以的。
回复

使用道具 举报

发表于 2009-3-14 22:22:06 | 显示全部楼层
你可以做个试验
function test takes unit u returns nothing
     call Wait(10.0)
     call Print(I2S(H2I(u)))
endfunction

function test2 takes unit u returns nothing
      call test(u)
      call Print("End")
endfunction

正常来说会出现
uHande
End
说明在test(u)执行完毕后才会接着往下走
回复

使用道具 举报

 楼主| 发表于 2009-3-14 22:22:14 | 显示全部楼层
也就是说,不管在call CreateUnitAtLoc(p,'hfoo',l,bj_UNIT_FACING)之前会等待多久,只要在call createu(p,l)之后清除掉就完成了排泄是么,这样不会影响创建单位么?因为那两个变量已经没有了...点也被移除了...
回复

使用道具 举报

 楼主| 发表于 2009-3-14 22:24:10 | 显示全部楼层
引用第4楼eff于2009-03-14 22:22发表的  :
你可以做个试验
function test takes unit u returns nothing
    call Wait(10.0)
    call Print(I2S(H2I(u)))
endfunction

function test2 takes unit u returns nothing
      call test(u)
      call Print("End")
endfunction

正常来说会出现
uHande
End
说明在test(u)执行完毕后才会接着往下走
嗯...以前做过类似的试验,不过当时得出的结论是两边同时走,也就是说如果test里含有循环或等待时,不会等它全部执行完才去执行call Print("End")

但是如果真的是同时走,就有一些东西不能解释了,比如条件的判断,又比如
local unit u=GetTriggerUnit()
local player p=GetOwningPlayer(u)
local location l=GetUnitLoc(u)
因为申明变量的速度要快于调用函数去获取返回值的速度
所以很迷惑啊...难道以前的测试有误么...
回复

使用道具 举报

发表于 2009-3-14 22:30:05 | 显示全部楼层
而且test(u)这个叫做形式参数,不需要清空。。
形参和实参不一样
回复

使用道具 举报

发表于 2009-3-14 22:32:05 | 显示全部楼层
你确定end会出现在wait之前?
那么就是wait干坏事了,坚决不能用wait
回复

使用道具 举报

 楼主| 发表于 2009-3-14 22:33:21 | 显示全部楼层
嗯...大致了解了...也就是说只需要在set u=null下面接着set p=null即可
不用在call CreateUnitAtLoc(p,'hfoo',l,bj_UNIT_FACING)的下面再次set null对么?
回复

使用道具 举报

发表于 2009-3-14 23:19:47 | 显示全部楼层
1. handle类型的局部变量,例如location等,用完后除了要删除,还需要set null,否则handle值不能回收

2. 函数的参数,不需要set null,对参数而言,set null没有任何作用

3. 全局变量没有必要set null,因为下次当这个变量再赋值的时候,原先的值就回收了

4. 给局部或者全局变量直接set另外一个值,等价于先set null再set其它值。所以循环里不需要set null

5. set null的效果,需要在等待或者触发运行结束以后才会表现出来。
回复

使用道具 举报

发表于 2009-3-15 00:20:01 | 显示全部楼层
hmmm,做了点简单的示意图,记得最开始看到类似这样的示意图是当初程序课时老师画的

初始状况是这样的,有6个点1、2、3、4、5、6(右边)和3个点变量l1、l2、l3(左边),我们很理想的认为这些点都可以有渠道直接得到(因为实际中,点变量只要没有在创建时直接赋值,后面就无法再找到它了,也就是所谓的泄漏)
1.PNG

我们进行第一次赋值,把3赋给l1,1赋给l2,6赋给l3,这样,变量和点之间出现了引用(红线)
2.PNG

我们进行下一次赋值,把1赋给l3,把4赋给l1,这样,引用出现了变化
3.PNG

我们把1、4、6清除掉,这样,点本身出现了变化
4.PNG

最后,我们把l1、l3设为null,这样l1和l3的引用被去掉了
5.PNG

此时1本身为空但有一个引用l2,所以无法被回收
2、3、5本身不为空,也无法被回收
4、6本身为空且没有引用,所以能够被回收
回复

使用道具 举报

 楼主| 发表于 2009-3-15 02:41:39 | 显示全部楼层
嗯...这是排泄的基本原理
当一个再也不会被使用到的handle存在数据或存在引用时,就产生内存泄漏
但是这和传递参数有什么关系呢...

传递参数既不是申明的过程也不是赋值的过程吧...
如果传递的是变量,则仅仅是把一个指针,也就是上图里的红线,传递给了另外一个函数
此时同样意义的红线分别存在于两个地方,一是创建的地方,二是被传递到的地方
这时如果变量所指代的内容(即数据)被清空,
而变量的内存地址引用(即红线)在任意一处地方没有被清空,
则没有被清空的那一处岂不是泄漏了...(至少泄漏了7个字节)
回复

使用道具 举报

发表于 2009-3-15 12:55:09 | 显示全部楼层
引用第12楼灼眼のsaber于2009-03-15 02:41发表的  :
嗯...这是排泄的基本原理
当一个再也不会被使用到的handle存在数据或存在引用时,就产生内存泄漏
但是这和传递参数有什么关系呢...

传递参数既不是申明的过程也不是赋值的过程吧...
.......

每个handle都有一个称为“引用计数”的数据(就是连到自己的“红线”数目),当handle代表的资源被释放,而且“红线”数目为0的时候,handle本身就被回收了。
(貌似这个很久之前GA就有人研究出来了……)

魔兽虚拟机中,给函数传递参数和赋值、声明等用的指令不同。

声明局部变量用local指令,赋值用set指令,而给函数传递参数是用push和poparg指令(参数压栈,然后在函数内部用poparg弹出参数变量)

set指令会影响handle的引用计数,但是push和poparg指令却不影响(?),所以函数参数不需要清理
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 点一下

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2024-11-25 11:02 , Processed in 0.036710 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表