Delphi 这是Rio上System.Net.HttpClient中的错误吗?
这是Delphi Rio中的函数,位于Delphi 这是Rio上System.Net.HttpClient中的错误吗?,delphi,firemonkey,delphi-10.3-rio,Delphi,Firemonkey,Delphi 10.3 Rio,这是Delphi Rio中的函数,位于System.Net.HttpClient THTTPClientHelper = class helper for THTTPClient .... procedure THTTPClientHelper.SetExt(const Value); var {$IFDEF AUTOREFCOUNT} LRelease: Boolean; {$ENDIF} LExt: THTTPClientExt; begin if FHTTPClientLis
System.Net.HttpClient
THTTPClientHelper = class helper for THTTPClient
....
procedure THTTPClientHelper.SetExt(const Value);
var
{$IFDEF AUTOREFCOUNT}
LRelease: Boolean;
{$ENDIF}
LExt: THTTPClientExt;
begin
if FHTTPClientList = nil then
Exit;
TMonitor.Enter(FHTTPClientList);
try
{$IFDEF AUTOREFCOUNT}
LRelease := not FHTTPClientList.ContainsKey(Self);
{$ENDIF}
LExt := THTTPClientExt(Value);
FHTTPClientList.AddOrSetValue(Self, LExt);
{$IFDEF AUTOREFCOUNT}
if LRelease then __ObjRelease;
{$ENDIF}
finally
TMonitor.Exit(FHTTPClientList);
end;
end;
这家伙想用LRelease
做什么
{$IFDEF AUTOREFCOUNT}
LRelease := not FHTTPClientList.ContainsKey(Self);
{$ENDIF}
LExt := THTTPClientExt(Value);
FHTTPClientList.AddOrSetValue(Self, LExt);
{$IFDEF AUTOREFCOUNT}
if LRelease then __ObjRelease;
{$ENDIF}
因此,如果FHTTPClientList
不包含THTTPClient
则将其添加到FHTTPClientList
中,然后将其refcount减少1。为什么要将其refcount减少一个??THTTPClient
仍处于活动状态,为什么要破坏它的refcount?他们在这里是一个bug,可能是那个家伙打错了,但我不明白他最初想做什么
有关如何从字典中删除项的信息:
procedure THTTPClientHelper.RemoveExt;
begin
if FHTTPClientList = nil then
Exit;
TMonitor.Enter(FHTTPClientList);
try
FHTTPClientList.Remove(Self);
finally
TMonitor.Exit(FHTTPClientList);
end;
end;
上述代码中ARC编译器手动引用计数的目的是模拟具有弱引用的字典。Delphi泛型集合由泛型数组支持,泛型数组将在ARC编译器上保存对添加到集合中的任何对象的强引用 有几种方法可以实现弱引用—使用指针,在对象被声明为弱引用的位置使用包装器,以及在适当的位置手动计算引用 使用指针会失去类型安全性,包装器需要更多的代码,所以我猜上面代码的作者选择了手动引用计数。那部分没问题 但是,正如您所注意到的,该代码中有一些可疑之处—虽然
SetExt
例程编写正确,但RemoveExt
有一个错误,导致以后崩溃
让我们在ARC编译器的上下文中浏览代码(为了简洁起见,我将省略编译器指令和无关的代码):
由于将对象添加到集合(数组)中会增加引用计数,为了实现弱引用,我们必须减少添加的对象实例的引用计数-这样实例的引用计数在存储到集合中后将保持不变。接下来,当我们从这样的集合中移除对象时,我们必须恢复引用计数平衡并增加引用计数。此外,我们还必须确保在销毁对象之前将其从此类集合中删除—这样做的好地方是析构函数
添加到集合:
LRelease := not FHTTPClientList.ContainsKey(Self);
FHTTPClientList.AddOrSetValue(Self, LExt);
if LRelease then __ObjRelease;
if FHTTPClientList.ContainsKey(Self) then
begin
__ObjAddRef;
FHTTPClientList.Remove(Self);
end;
destructor THTTPClient.Destroy;
begin
RemoveExt;
inherited;
end;
我们将对象添加到集合中,然后在集合持有对对象的强引用后,我们可以释放它的引用计数。如果对象已经在集合中,这意味着它的引用计数已经减少,我们不能再减少它-这就是LRelease
标志的目的
从收藏中删除:
LRelease := not FHTTPClientList.ContainsKey(Self);
FHTTPClientList.AddOrSetValue(Self, LExt);
if LRelease then __ObjRelease;
if FHTTPClientList.ContainsKey(Self) then
begin
__ObjAddRef;
FHTTPClientList.Remove(Self);
end;
destructor THTTPClient.Destroy;
begin
RemoveExt;
inherited;
end;
如果对象在集合中,我们必须在从集合中删除对象之前恢复平衡并增加引用计数。这是RemoveExt
方法中缺少的部分
确保销毁时对象不在列表中:
LRelease := not FHTTPClientList.ContainsKey(Self);
FHTTPClientList.AddOrSetValue(Self, LExt);
if LRelease then __ObjRelease;
if FHTTPClientList.ContainsKey(Self) then
begin
__ObjAddRef;
FHTTPClientList.Remove(Self);
end;
destructor THTTPClient.Destroy;
begin
RemoveExt;
inherited;
end;
注意:为了使这种伪造的弱集合能够正常工作,必须仅通过上述方法添加和删除项目,这些方法负责平衡引用计数。使用任何其他原始采集方法(如Clear
)将导致引用计数中断
是否存在Bug? 在
System.Net.HttpClient
code-breakedRemoveExt
中,方法仅在析构函数中调用,而且FHTTPClientList
是私有变量,不会以任何其他方式更改。乍一看,该代码工作正常,但实际上包含相当微妙的bug
要解开真正的bug,我们需要涵盖可能的使用场景,从几个已确定的事实开始:
FHTTPClientList
字典中,只有SetExt
和RemoveExt
方法可以更改内容和引用项计数SetExt
方法正确\uuu ObjAddRef
的breakedRemoveExt
方法只在THTTPClient
析构函数中调用,这就是这个微妙的错误的根源FRefCount
变量上应用objDestroyingFlag
来确保的,更改其值和任何进一步增加/减少的计数将不再导致启动销毁过程的特殊值0
,因此对象是安全的,不会被销毁两次
在上面的代码中,当调用THTTPClient
析构函数时,这意味着对对象实例的最后一个强引用已超出范围或被设置为nil
,此时唯一剩余的能够触发引用计数机制的活动引用是FHTTPClientList
中的引用。该引用已被RemoveExt
方法清除(断开或不断开),如前所述,这无关紧要。一切正常
但是,代码的作者忘记了触发析构函数的一个很小的方法-dispeof
,但此时对象实例还没有达到其引用计数生存期。换句话说,如果析构函数是由dispeof
调用的,则任何后续的引用计数触发器都必须平衡,因为在析构函数链调用完成后,仍有对对象的活动引用将触发引用计数机制。如果我们在这一点上打破计数,结果将是灾难性的
由于THTTPClient
不是t组件
的后代,因此需要处理
很容易疏忽,忘记某人,无论如何,某个地方都可以对此类变量调用DipsoseOf
——例如,如果您创建了THTTPClient
实例的自有列表,则清除此类列表将