这是在System.Net.HttpClient中的Delphi Rio中找到的功能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中,然后将其引用计数减少1。为什么将其引用计数减少一个? THTTPClient仍然有效,并使用了为什么破坏它的引用计数?他们在这里是个虫子,也许那个人打错了字,但我不明白他最初想做什么...有关信息,这是从字典中删除项目的方式: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;我们将对象添加到集合中,然后在集合对我们的对象拥有强大的引用之后,我们可以释放它的引用计数。如果对象已经在集合中,则意味着它的引用计数已经减少,我们不能再次减少它-这是LRelease标志的目的。从收藏中删除:if FHTTPClientList.ContainsKey(Self) then begin __ObjAddRef; FHTTPClientList.Remove(Self); end;如果对象在收集中,则必须从收集中删除对象之前恢复余额并增加引用计数。这是RemoveExt方法中缺少的部分。确保销毁对象不在列表中:destructor THTTPClient.Destroy;begin RemoveExt; inherited;end;注意:为了使这种伪造的弱集合正常工作,必须仅通过上述方法来平衡引用计数,从而添加和删除项目。使用任何其他原始收集方法(例如Clear)将导致引用计数中断。有没有bug?在System.Net.HttpClient代码中,损坏的RemoveExt方法仅在析构函数中调用,FHTTPClientList也是私有变量,并且不能以任何其他方式更改。乍一看,该代码可以正常运行,但实际上包含了相当细微的错误。为了揭示真正的错误,我们需要从几个已确定的事实开始,涵盖可能的使用场景:只有更改内容并通过FHTTPClientList词典中的项目的引用计数的方法是SetExt和RemoveExt方法SetExt方法正确不调用RemoveExt的损坏的__ObjAddRef方法仅在THTTPClient析构函数中被调用,这就是此细微错误的来源。当在任何特定的对象实例上调用析构函数时,这意味着该对象实例已达到生存期,并且任何后续引用计数触发器(在析构函数执行期间)都不会影响代码的正确性。通过在objDestroyingFlag变量上应用FRefCount更改其值来确保这一点,并且任何进一步的计数增加/减少都将不再导致特殊值0启动销毁过程-因此对象是安全的,不会被销毁两次。在上面的代码中,当调用THTTPClient析构函数时,这意味着对对象实例的最后一个强引用已超出范围或被设置为nil,并且此时唯一可以触发引用计数机制的剩余活动引用就是。该引用已通过FHTTPClientList方法清除(无论是否断开),如先前所说的无关紧要。而且一切正常。但是,代码的编写者忘记了一个微小的东西-RemoveExt方法,该方法会触发析构函数,但此时,对象实例尚未达到其引用计数生存期。换句话说-如果DisposeOf调用了析构函数,则必须平衡所有后续引用计数触发器,因为仍然存在对对象的实时引用,这些对象将在析构函数链调用完成后触发引用计数机制。如果我们在那个时候中断计数,结果将是灾难性的。由于DisposeOf不是需要THTTPClient的TComponent后代,因此很容易进行疏忽而忘记了某个人,无论如何,某处仍可以在该变量上调用DisposeOf-例如,如果您创建了DipsoseOf实例的所有者列表清除此类列表将在它们上调用THTTPClient并愉快地破坏其引用计数,因为DisposeOf方法最终被破坏了。结论:是的,这是一个错误。
10-05 22:14