在写此章之前,我在编写Jass时,在缓存方面遇到很多的问题,特别是配合ReturnBug系统后,这方面的问题显得更加的棘手。这些问题是什么呢? —— 缓存冲突。
这里的缓存冲突并非是指数据上的冲突。而是一个令人非常尴尬的现像。
比如,当我要作一个技能时,我用缓存记录了A单位,标记为 "A"。过一会,我把A单位删除,直接使用"A"标记记录B单位。这时,"A"标记代表的是B单位。当然,这种情况是很正常的。但在使用ReturnBug后,我们所记录的是单位的整数地址(详见WOW专用编辑器),也就是一个整数纺编号,这个整数编号在没有清空之前,是一直缓存着的。透过“整数地址”我可以得到A单位的整数地址为(假设例举):100486,而B单位的整数地址为:100488,当我们直接删除A单位后,100486所代表的数据为空,按照War3操作HandleTable的规则,B单位的整数地址有时候会“意外”的上调两位,也就是变成了:100486。嗯,这儿就是要点了,当我们没有清空缓存中的"A"标记中所记录的整数地址时,我们在删除A单位后,如果意外地再次使用"A"标记,那么我们很可能就会使用了B单位(当然,这种现像是随机的,因为HandleTable的变化是随机的)。这种情况下,就容易导致许多问题的出现,通常这种现像有人称之为“缓存遗漏”现像,但实际上,并非是缓存被漏掉了,而是HandleTable产生了变化,导到了一些数据指向的变化。这种变化我暂时定义为“缓存与内存的冲突”,简称为“缓存冲突”。
缓存冲突的现像是接触到缓存以及ReturnBug后经常碰到的问题,当然,如果你没有排除内存泄漏的习惯,这个问题发生的几率比较小。但这个问题的存在,引起了不少的麻烦,在我所做的大部分技能中,都采用了"ReturnBugSystem"的缓存体系。有时候测试时,发生一些“怪异”的现像,所以我研究了半天,终于找到一条能够解决这种现像的方法。
请看以下代码:
[jass]
globals
// -----------------------------------------------------------------
integer array var_Timer_Int
timer array var_Timer
integer array var_Unit_Int
unit array var_Unit
// -----------------------------------------------------------------
endglobals
function Unit_GetNull takes nothing returns integer
// 取得一个空变量位,反回空变量位的索引
local integer i = 0
local integer var = 0
local boolean exit = false
loop
if var_Unit == null then
set var = i
set exit = true
endif
set i = i + 1
exitwhen exit
endloop
return var
endfunction
function Unit_GetInt takes unit u returns integer
// 取得一个变量索引
local integer i = 0
local integer var = 0
local boolean exit = false
loop
if var_Unit == u then
set var = i
set exit = true
endif
set i = i + 1
exitwhen exit
endloop
return var
endfunction
function Unit_GetUnit takes integer i returns unit
// 按引取得变量
return var_Unit
endfunction
function Unit_SetUnit takes unit u returns integer
// 动态记录变量,反回所记录数据的索引
local integer i = Unit_GetNull()
set var_Unit = u
set var_Unit_Int = 14856 + i
return i
endfunction
function Unit_FlushInt takes integer i returns nothing
// 清空变量
set var_Unit = null
set var_Unit_Int = 0
endfunction
[/jass]
以上是一个储存临时单位的动态数据系统的示例。为什么称之为动态,是因为可以按照当前数据的情况来灵活分配所记录数据的索引,我们可以使用索引来取得记录一数据。为什么不用缓存记录变量呢,因为变量速度、效率上比缓存快得多。比如。我们记录N个单位,我们只需要记录单位的变量索引,而不是这个单位。通过索引,我们可以取回所记录的数据。变量索引是动态的,当数据不需要使用时,用清空功能就可以释放变量,释放后的变量又可以用来记录新的单位。这个用途,原理上是和War3中的HandleTable很想似的,只不过我们是给的固定索引,在我们没有清理这些索引变量前,这组变量是不能重新用来记录数据的。
通过以前的代码,我们可以轻易的在局部代替ReturnBug+GameCache,减少出错的几率。
//这是用来清理存储位置信息的函数
function FlushTimerInfo takes timer t returns nothing
local integer tmIndex = GetTimerIndex(t)
set SYS_TimerData_TMDLG[tmIndex] = null
set SYS_TimerData_UnitGroup[tmIndex] = null
set SYS_TimerData_I00[tmIndex] = 0
set SYS_TimerData_I01[tmIndex] = 0
set SYS_TimerData_I02[tmIndex] = 0
set SYS_TimerData_I03[tmIndex] = 0
set SYS_TimerData_I04[tmIndex] = 0
set SYS_TimerData_I05[tmIndex] = 0
set SYS_TimerData_I06[tmIndex] = 0
set SYS_TimerData_I07[tmIndex] = 0
set SYS_TimerData_I08[tmIndex] = 0
set SYS_TimerData_I09[tmIndex] = 0
//======实数======================================
set SYS_TimerData_R00[tmIndex] = 0.00
set SYS_TimerData_R01[tmIndex] = 0.00
set SYS_TimerData_R02[tmIndex] = 0.00
set SYS_TimerData_R03[tmIndex] = 0.00
set SYS_TimerData_R04[tmIndex] = 0.00
set SYS_TimerData_R05[tmIndex] = 0.00
set SYS_TimerData_R06[tmIndex] = 0.00
set SYS_TimerData_R07[tmIndex] = 0.00
set SYS_TimerData_R08[tmIndex] = 0.00
set SYS_TimerData_R09[tmIndex] = 0.00
//======单位=======================================
set SYS_TimerData_U00[tmIndex] = null
set SYS_TimerData_U01[tmIndex] = null
set SYS_TimerData_U02[tmIndex] = null
set SYS_TimerData_U03[tmIndex] = null
set SYS_TimerData_U04[tmIndex] = null
set SYS_TimerData_U05[tmIndex] = null
set SYS_TimerData_U06[tmIndex] = null
set SYS_TimerData_U07[tmIndex] = null
set SYS_TimerData_U08[tmIndex] = null
set SYS_TimerData_U09[tmIndex] = null
//======物品========================================
set SYS_TimerData_IT00[tmIndex] = null
set SYS_TimerData_IT01[tmIndex] = null
set SYS_TimerData_IT02[tmIndex] = null
set SYS_TimerData_IT03[tmIndex] = null
set SYS_TimerData_IT04[tmIndex] = null
set SYS_TimerData_IT05[tmIndex] = null
set SYS_TimerData_IT06[tmIndex] = null
set SYS_TimerData_IT07[tmIndex] = null
set SYS_TimerData_IT08[tmIndex] = null
set SYS_TimerData_IT09[tmIndex] = null
//======特效========================================
set SYS_TimerData_E00[tmIndex] = null
set SYS_TimerData_E01[tmIndex] = null
set SYS_TimerData_E02[tmIndex] = null
set SYS_TimerData_E03[tmIndex] = null
set SYS_TimerData_E04[tmIndex] = null
set SYS_TimerData_E05[tmIndex] = null
set SYS_TimerData_E06[tmIndex] = null
set SYS_TimerData_E07[tmIndex] = null
set SYS_TimerData_E08[tmIndex] = null
set SYS_TimerData_E09[tmIndex] = null
endfunction
function InitTimerSlotArray takes nothing returns nothing
local integer i = 1
local timer t = CreateTimer()
set SYS_TimerMaxNum = 8000
set SYS_TimerStartLoc = H2I(t)
set SYS_TimerSlotArray[0] = 0
loop
exitwhen(i>SYS_TimerMaxNum)
set t = CreateTimer()
set SYS_Timer = t
set SYS_TimerSlotArray = i
set i = i + 1
endloop
set SYS_TimerSlotArrayP = SYS_TimerMaxNum
set SYS_TimerSlotArrayP2 = 0
set SYS_TimerArrayFlag = true
set SYS_TimerNum = 0
endfunction
//从timer堆中获得一个空闲Timer的函数
function GetTimer takes nothing returns timer
local integer tIndex
if(SYS_TimerArrayFlag)then
set tIndex = SYS_TimerSlotArray[SYS_TimerSlotArrayP]
set SYS_TimerSlotArrayP = SYS_TimerSlotArrayP - 1
if(SYS_TimerSlotArrayP<=0)then
set SYS_TimerArrayFlag = not(SYS_TimerArrayFlag)
endif
else
set tIndex = SYS_TimerSlotArray2[SYS_TimerSlotArrayP2]
set SYS_TimerSlotArrayP2 = SYS_TimerSlotArrayP2 - 1
if(SYS_TimerSlotArrayP2<=0)then
set SYS_TimerArrayFlag = not(SYS_TimerArrayFlag)
endif
endif
if(tIndex>=0 and tIndex<=SYS_TimerMaxNum)then
return SYS_Timer[tIndex]
else
return null
endif
endfunction
//释放不再使用的Timer
function ReleaseTimer takes timer t returns nothing
call PauseTimer(t)
call FlushTimerInfo(t)
if(not(SYS_TimerArrayFlag))then
set SYS_TimerSlotArrayP = SYS_TimerSlotArrayP + 1
set SYS_TimerSlotArray[SYS_TimerSlotArrayP] = H2I(t) - SYS_TimerStartLoc
else
set SYS_TimerSlotArrayP2 = SYS_TimerSlotArrayP2 + 1
set SYS_TimerSlotArray2[SYS_TimerSlotArrayP2] = H2I(t) - SYS_TimerStartLoc
endif
endfunction[/jass]