QMacros宏的稳定性,系统给了一个枚举类型TQMacroVolatile来表示,它定义的正确与否,会影响整体替换操作的效率。我们先看其定义:
/// <summary>宏定义值的稳定性定义</summary> /// <param name="mvImmutable">值是固定不变的,是一种常量状态</param> /// <param name="mvStable">值是相对不变的,在一次替换操作过程中,我们可以认为其等价发mvImmutable</param> /// <param name="mvVolatile">值是易变的,每次获取宏的值时,返回的值都可能是不同的</param> TQMacroVolatile = (mvImmutable, mvStable, mvVolatile);
这三种不同的选择,影响替换时,QMacros如何优化操作过程:
1、不变的(mvImmutable)
此时,值是固定不变的,不管宏什么时候调用,返回值始终是固定的。如定义宏PI返回π的值,它无论何时调用都返回固定的值。
默认通过下面的Push函数入栈的宏都是mvImmutable类型的宏。
procedure Push(const AName, AValue: QStringW);
如果通过为宏提供了事件处理函数并指明其值类型为mvImmutable,宏在实际入栈时,会缓存一次宏的值,供以后调用。无论后面多少次替换,都只使用第一次的初始值,这样可以减少不必要的调用开销,加快替换操作的完成速度。
procedure Push(const AName: QStringW; AOnFetchValue: TQMacroValueFetchEvent;AVolatile: TQMacroVolatile = mvVolatile; ATag: IntPtr = 0);
我们看一下这个函数入栈时的部分代码:
function NewValue: PQMacroValue; begin New(Result); Result.OnFetchValue := AOnFetchValue; Result.Prior := nil; Result.Next := nil; Result.Tag := ATag; Result.SavePoint := FSavePoint; Result.Volatile := AVolatile; case AVolatile of mvImmutable: // 始终返回固定值的宏,我们直接取一次值,以便优化其替换过程 AOnFetchValue(Self, AItem, #0, Result.Value); mvStable: Inc(FStableCount); mvVolatile: Inc(FVolatileCount); end; end;
可以看到mvImmutable类型的函数入栈时直接获取了其值。而在内部获取宏的取值的过程中,代码检查到宏的稳定性为muImmutable时,会直接返回缓存的Value值。
if AItem.Value.Volatile = mvImmutable then AValue := AItem.Value.Value else if Assigned(AItem.Value.OnFetchValue) then begin if (AItem.Value.Volatile = mvVolatile) or (FReplaceRef = 0) then // AItem.Value.OnFetchValue(Self, AItem, AQuoter, AValue) else // mvStable begin if AItem.Value.SavePoint <> FSavePoint then // 缓存当前值 begin New(AItem.Value.Next); AItem.Value.Next.Tag := AItem.Value.Tag; AItem.Value.Next.OnFetchValue := nil; AItem.Value.Next.SavePoint := FSavePoint; AItem.Value.Next.Volatile := mvImmutable; AItem.Value.Next.Prior := AItem.Value; AItem.Value.Next.Next := nil; AItem.Value.OnFetchValue(Self, AItem, #0, AValue); AItem.Value.Next.Value := AValue; AItem.FValue := AItem.Value.Next; end; end; end
2、稳定的(mvStable)
这类宏的值在同一次替换过程中,多次调用会返回同一个值,如取当前时间类函数,在替换过程中,一般我们是不变的,所以它的稳定性我们一般设置为mvStable。
因为这类值在单次替换时,其值是固定不变的,所以,替换操作对此的优化是发现是第一次访问该宏时,缓存该值为并入栈为mvImmutable,这样子后面读取该宏的值时,都和mvImmutable的处理完全一样,从而不再需要进行函数调用。
3、易变的(mvVolatile)
这类宏的值是易变的,每次调用都会返回完全不同的值。这类函数由于每次宏替换时都返回不同的值,因此,在替换时我们需要按最糟糕的情况来考虑。每次需要替换时,都会调用OnFetchValue对应的回调函数来获取该值。
综上所述,我们可以总结如下:
(1)对于mvImmutable类型的宏,其OnFetchValue只在注册时触发一次,以后不再触发。
(2)对于mvStable类型的宏,其OnFetchValue在每次执行Replace函数时触发,在同一次替换操作时,不会重复触发。
(3)对于mvVolatile类型的宏,其OnFetchValue在每次遇到需要解析该宏的值时都会触发。