{"id":876,"date":"2012-06-06T09:07:57","date_gmt":"2012-06-05T21:07:57","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=876"},"modified":"2012-06-06T13:15:45","modified_gmt":"2012-06-06T01:15:45","slug":"commonest-weakness","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/876\/","title":{"rendered":"Commonest Weakness&#8230;"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>Stefan Glienke recently added <a href=\"http:\/\/delphisorcery.blogspot.co.nz\/2012\/06\/weak-interface-references.html\">his contribution to the various solutions for implementing weak interface references in Delphi<\/a>.<br \/>\n<!--more--><br \/>\nHe quite neatly and comprehensively covers the need for such things, but his approach requires generics support and so is limited to later versions of the compiler (just how late really depends on how confident you are in the robustness of the generics implementation in each iteration of the Delphi compiler since their introduction).<\/p>\n<p>So I thought I would share my own approach that I have been using for some time and which is usable in all versions of Delphi from 3 (where-in interfaces were introduced).<\/p>\n<p>First, an example of the approach in action using Stefan&#8217;s own example as the basis for my own:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n\r\ntype\r\n  TParent = class(TInterfacedObject, IParent)\r\n  private\r\n    fChildren: TInterfaceList;\r\n    procedure AddChild(const aChild: IChild);\r\n  public\r\n    destructor Destroy; override;\r\n  end;\r\n\r\n  TChild = class(TInterfacedObject, IChild)\r\n  private\r\n    fParentRef: TWeakInterface;\r\n    function get_Parent: IParent;\r\n  public\r\n    constructor Create(const aParent: IParent);\r\n    destructor Destroy; override;\r\n    property Parent: IParent read get_Parent;\r\n  end;\r\n\r\n  \/\/ The implementation of TParent adding a child to it's list\r\n  \/\/  should be sufficiently obvious as to not need illustration\r\n  \/\/  so I shall show only the use of TWeakInterface in TChild\r\n\r\n  constructor TChild.Create(const aParent: IParent);\r\n  begin\r\n    inherited Create;\r\n\r\n    fParentRef := TWeakInterface.Create(aParent);\r\n  end;\r\n\r\n  destructor TChild.Destroy;\r\n  begin\r\n    fParentRef.Free;\r\n    inherited;\r\n  end;\r\n\r\n  function TChild.get_Parent: IParent;\r\n  begin\r\n    result := fParentRef as IParent;\r\n  end;\r\n\r\n<\/pre>\n<p>Since the parent is maintaining strong references to the children (ensuring that as long as the parent survives then it&#8217;s children do also), what we need to take care of (and the whole point of this and Stefan&#8217;s post) is that the children themselves <em>do not hold strong references <strong>back<\/strong> to the parent<\/em>.<\/p>\n<p>I achieve this using a <em>TWeakInterface<\/em>.<\/p>\n<p><em>TWeakInterface<\/em> is essentially an encapsulation of the technique found quite commonly in the VCL itself (as mentioned by Stefano in the comments on Stefan&#8217;s post) of holding an interface reference as a plain pointer value. Stefan rightly points out some dangers of this approach, and the advantage of using an encapsulation such as <em>TWeakInterface<\/em> in this way is that it enables us to address some of these.<\/p>\n<p>With a plain pointer value, the interface it stores is retrieved by hard-casting back to the original interface type:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n\r\n  \/\/ Taking a weak reference from a strong one:\r\n  parentRef := Pointer(someIParent);\r\n\r\n  \/\/ Obtaining a strong reference from a weak one:\r\n  parent := IParent(someIParent);\r\n\r\n<\/pre>\n<p>The potential for mistakes here should be obvious.<\/p>\n<p>When casting the strong reference from the pointer you have to be sure to use the correct interface reference. Use the wrong one (even, for example, an interface that is derived &#8211; i.e. inherits &#8211; from the actual interface reference that is held) and all sorts of things could potentially go wrong.<\/p>\n<p>So one thing that <em>TWeakInterface<\/em> does is treat the encapsulated reference it is provided with as <strong>IUnknown<\/strong> (or <strong>IInterface<\/strong>, if you prefer; Potayto \/ Potahto).<\/p>\n<p>It also does not expose the &#8220;captured&#8221; reference at all.<\/p>\n<p>To get the interface reference back out from <em>TWeakInterface<\/em> you have to ask for it.<\/p>\n<p>Nicely. \ud83d\ude42<\/p>\n<p><em>TWeakInterface<\/em> is itself an interfaced object, but it delegates the entire implementation of <strong>IUnknown<\/strong> to the captured interface reference so that when you write:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n\r\n  result := fParentRef as IParent;\r\n\r\n<\/pre>\n<p>It is the captured interface reference that is asked for the requested interface (<strong>ref.QueryInterface<\/strong>), rather than a hard type-cast being performed.<\/p>\n<p>Thus if you ask for an interface that the weakly referenced object does not support you will get the exception that you so richly deserve.<\/p>\n<p>It also means that you can even ask for an interface that is different from the one you captured, and as long as the referenced object also implements that interface then that also will be fine.<\/p>\n<p>What this approach does not take care of is the situation when the weakly referenced object is destroyed, leaving a &#8220;dangling&#8221; weak reference to itself.<\/p>\n<p>As Stefano points out in his reply to this observation, this situation should not occur when using weak references as the very fact that you *have* this potential means that you should have mechanisms in place to deal with it.<\/p>\n<p>e.g. if a child&#8217;s parent can be destroyed from out above it&#8217;s head, then the child will almost certainly need to know when that happens. <strong>NIL<\/strong>&#8216;ing the reference to it&#8217;s parent is probably the least significant thing it will need to take into account in that situation.<\/p>\n<p>In my own code I have <a href=\"https:\/\/www.deltics.co.nz\/blog\/?p=160\">a multi-cast mechanism available for precisely this &#8220;notify destruction&#8221; purpose<\/a>, and I could easily have incorporated this into my <em>TWeakInterface<\/em> implementation.<\/p>\n<p>That is, in the constructor for <em>TWeakInterface<\/em> it could query the passed interface for <em>IOn_Destroy<\/em> support, and if present add a listener which would <strong>NIL<\/strong> the captured reference when necessary. <em>TWeakInterface<\/em> would then also have to implement <em>IOn_Destroy<\/em>, but that is also a trivial matter.<\/p>\n<p>However, just as Stefano points out, in every instance that this would have been useful, the class incorporating a weak reference to some other object already implements (in my code) an <strong>On_Destroy<\/strong> mechanism for much broader purposes, assuming no other, more direct mechanism exists.<\/p>\n<p>So I decided against incorporating this in my weak interface reference encapsulation itself, keeping it as simple as possible.<\/p>\n<h3>In Summary, The Complete Class<\/h3>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n\r\n    TWeakInterface = class(TObject)\r\n    private\r\n      fRef: Pointer;\r\n      function get_Ref: IUnknown;\r\n    protected \/\/ IUnknown\r\n      property Ref: IUnknown read get_Ref implements IUnknown;\r\n    public\r\n      constructor Create(const aRef: IUnknown);\r\n    end;\r\n\r\n{ TWeakInterface --------------------------------------------------------------------------------- }\r\n\r\n  { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }\r\n  constructor TWeakInterface.Create(const aRef: IUnknown);\r\n  begin\r\n    inherited Create;\r\n\r\n    fRef := Pointer(aRef);\r\n  end;\r\n\r\n  { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }\r\n  function TWeakInterface.get_Ref: IUnknown;\r\n  begin\r\n    result := IUnknown(fRef) as IUnknown;\r\n  end;\r\n\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> Stefan Glienke recently added his contribution to the various solutions for implementing weak interface references in Delphi.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[4],"tags":[29,33,149],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-e8","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":391,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/391\/","url_meta":{"origin":876,"position":0},"title":"Free Yourself","date":"27 Sep 2008","format":false,"excerpt":"Barry Kelly recently posted an example of \"smart\" pointers (specifically the auto-pointer variant of a smart pointer) using generics in Delphi 2009.\u00a0 It was an interesting use of generics but the end result was something that has - in part at least - been possible for some time in Delphi\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":338,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/338\/","url_meta":{"origin":876,"position":1},"title":"Delphi 2009 &#8211; A Heads-Up for Low-Level Coders","date":"13 Sep 2008","format":false,"excerpt":"Prompted by a conversation with some colleagues where-in we collectively speculated about the implementation details of a generic class and what impact - if any - this might have on performance vs a \"traditional\" polymorphic equivalent, I threw together a quick performance test case in my Smoketest framework, and as\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":244,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/244\/","url_meta":{"origin":876,"position":2},"title":"Generic Methods and Type Inferencing","date":"25 Aug 2008","format":false,"excerpt":"In the new Delphi forums recently, Barry Kelly responded to a question about lambda expression syntax in Tibur\u00f3n with this observation: This syntax needs type inference. Our compiler was not originally written to support type inference, but work to support type inference is orthogonal to supporting anonymous methods. ...\u00a0 you'll\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1300,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1300\/","url_meta":{"origin":876,"position":3},"title":"What&#8217;s in a Name(space) ?","date":"28 Nov 2012","format":false,"excerpt":"The ever evolving DWScript project continues to advance the Pascal language at an impressive pace. Just today it was announced that this scripting version of Pascal now has \"namespace\" support. When I first read the details of the implementation, my initial reaction was that it \"felt a bit backwards\". The\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1397,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1397\/","url_meta":{"origin":876,"position":4},"title":"Qualified Enum Reference That Fails to Compile in XE4 (and rightly so)","date":"01 May 2013","format":false,"excerpt":"Running through some of my code last night, putting them through the new XE4 compiler, threw up a real oddity: Some code that used to compile just fine, which no longer compiles in XE4 and which should not have compiled before! It's an odd one, because the code previously compiled\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":349,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/349\/","url_meta":{"origin":876,"position":5},"title":"Delphi 2009 &#8211; String Performance","date":"18 Sep 2008","format":false,"excerpt":"NOTE: Downloads are now fixed! Andreas Hausladen generously took the time to make some detailed comments on my previous post, one of which prompted me to throw together some further performance test cases for String types specifically.\u00a0 The results were something of a mixed bag and contained some surprises. The\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/delphi2009-stringperformance-resultscapture.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/876"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=876"}],"version-history":[{"count":14,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/876\/revisions"}],"predecessor-version":[{"id":887,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/876\/revisions\/887"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=876"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=876"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=876"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}