weakref syntax to avoid circular reference

  1. last week

    Tim S

    Dec 2 Canterbury, UK
    Edited last week

    I have a socket class (mysocket) subclassed from SSLSocket with a property socktimer, in which I want to put a reference to my socketTimer class, which itself has a socket property in which I want to put a weak reference back to the socket. That way I avoid circular refs and all the relevant events (timerAction, socketDataAvailable, etc) will have access to the data they need to operate.

    The expectation is that the socket gets used, then closed, and goes out of scope as the thread using it returns. Then I would expect the socket to get removed followed by the timer, as their refcounts go to zero.

    The timer is instanced in the socket's Constructor, with this as my first attempt:

    Dim  tim as socketTimer = new socketTimer
    me.socktimer = tim                                     // In the socket, a reference to the timer
    tim.socket   = SSLSocket (weakref (me))                // In the timer, a reference to the socket

    Is this the right syntax for creating the weakref back to the socket? It compiles OK but I fear that all I've done is cast "me" twice.

    Judging by some of the examples in the doc and also threads here, it looks more like I should change the socket property in the socketTimer class to be of type weakref, and then proceed thus:

    Dim  tim as socketTimer = new socketTimer
    me.socktimer = tim                                     // In the socket, a reference to the timer
    tim.socket   = new weakref (me)                        // In the timer, a reference to the socket

    This also compiles and if correct then I assume that in the event handlers I would need to take socket.value and cast that to the appropriate socket class.

    Is this a correct understanding?

  2. Andrew L

    Dec 2 San Francisco, CA, USA

    Make the Timer's Action event a method of your SSLSocket class, and assign it to the Timer using AddHandler and WeakAddressOf. This will let you reference both the Timer and the SSLSocket from within the Timer.Action event, and using WeakAddressOf prevents a circular reference:

    Class MySocket
    Inherits SSLSocket
      
      Private Property SockTimer As Timer
    
      Sub Constructor()
        Super.Constructor() ' required when subclassing sockets
        SockTimer = New Timer
        ' this line assigns a weak reference of the handler method to the timer as its Action event handler
        AddHandler SockTimer.Action, WeakAddressOf TimerActionHandler
      End Sub
    
      Private Sub TimerActionHandler(Sender As Timer)
        ' within this handler, "Me" refers to the Socket and "Sender" refers to the Timer
      End Sub
    End Class
  3. Kem T

    Dec 3 Pre-Release Testers, Xojo Pro, XDC Speakers New York

    I agree with Andrew. If your Timer is tied to a specific class, you might as well use AddHandler and implement the code directly in the class. The only addition I'd make is to put the corresponding RemoveHandler into the Destructor.

  4. Kem T

    Dec 3 Pre-Release Testers, Xojo Pro, XDC Speakers New York

    But since your question is about WeakRef, this is how I handle it.

    In the subordinate class, I create a private shadow property, say, mParent, and set it's "Hidden" attribute. I then created a computed property, Parent, with this code:

    Get (value As MyClass)
      if mParent is nil then
        return nil
      else
        return MyClass( mParent.Value )
      end if
    
    Set (value As MyClass)
      if value is nil then
        mParent = nil
      else
        mParent = new WeakRef( value )
      end if

    You now have a property that directly references the other object without creating a circular reference.

    If you have many children that will reference the parent, create a property in the parent like MyWeakRef that stores and returns a single WeakRef to that object.

  5. Tim S

    Dec 3 Canterbury, UK

    @Andrew L Make the Timer's Action event a method of your SSLSocket class, and assign it to the Timer using AddHandler and WeakAddressOf. This will let you reference both the Timer and the SSLSocket from within the Timer.Action event, and using WeakAddressOf prevents a circular reference:

    Class MySocket Inherits SSLSocket Private Property SockTimer As Timer Sub Constructor() Super.Constructor() ' required when subclassing sockets SockTimer = New Timer ' this line assigns a weak reference of the handler method to the timer as its Action event handler AddHandler SockTimer.Action, WeakAddressOf TimerActionHandler End Sub Private Sub TimerActionHandler(Sender As Timer) ' within this handler, "Me" refers to the Socket and "Sender" refers to the Timer End Sub End Class

    I am in fact already doing all of this. I omitted that from my OP as it didn't seem pertinent to my question. And why does the use of WeakAddressOf (which I already do) help the circular reference issue? The circular reference issue, surely, is going to be caused by the the socket referencing the timer by having a property pointing at it, and likewise the timer having a property that refers back to the socket.

    And @Kem T I don't get the point at all, I'm afraid. I don't have many children referencing the parent, just the one, and the linkages between them are set up in the socket's constructor (that's my intention, anyway). The socket then gets used until the thread using it terminates, which should mean the socket's refcount goes to zero, which in turn causes the timer's refcount to go to zero.

    The weakref is set in the constructor and used in the timer's action event, where the code is:

    Sub TimerEvent (t as timer)
    
    Dim tim as socketTimer, socket as mySocket
    
    tim = socketTimer (t)
    socket = mySocket (tim.socket.value)
    
    // code to resume thread follows
    
    End Sub

    Again, the compiler seems happy but I haven't tried this yet.

  6. Kem T

    Dec 3 Pre-Release Testers, Xojo Pro, XDC Speakers New York

    If you set up AddHandler with "AddressOf" instead of "WeakAddressOf", you've created a reference and that object will never go out of scope.

    I'm not sure which point you mean as I made several. Forgetting the one about "multiple children", I was describing a system to manage WeakRefs where you can access the object directly (and through the debugger) without having to manage the WeakRef yourself each time. It's meant for convenience and readability more than function.

  7. Tim S

    Dec 3 Canterbury, UK

    OK - I'll study the AddressOf/WeakAddressOf issue a bit more.

    Regarding the computed property and its get/set methods, I think in my case I would be using get once only and set once only, so in this instance that, to me, seems a bit heavy as each is a one-liner. But I can see the utility in cases where one might be doing set/get in many places in the code or what is needed to actually effect the get/set is many lines of code.

    Thanks.

or Sign Up to reply!