Hi Peter, Hi RB,
I have a few thoughts on this topic. Overall, RB, I agree with your
conclusions and the approach you are taking. But I'll comment point by point
below:
There are 3 area's where I possibly could use this...
One, holding references to certain nodes in a treeview in a simple VB
collection. Currently I do this by storing
the full references to the nodes and there isn't really a problem with that.
Yes, using standard, strong references is the *correct* way to do this. I
would strongly advise not to play with weak references unless you absolutely
have to. It is extremely rare to need them, and it is precariously difficult
to implement correctly when using VB 6.0 or VBA.
Secondly, I use a number of collection classes to have easy access to all
the various controls on my VBA userforms.
Again these hold the full references to the controls and instead I could
just store the ObjPtr instead. Possibly doing
that could save a bit of memory, but not worth the extra trouble.
It wouldn't save you any memory, actually. Either way you are storing 32
bits on a 32 bit machine. Definitely a lot of extra trouble. And controls are
not prone to circular reference problems.
Thirdly, I often pass object references to a VB6 ActiveX dll and again I
could instead pass the ObjPtr and restore to the full reference in the dll.
Again not sure any benefit in doing that.
Agreed, I can't see any benefit to this either. The danger in passing around
weak references like this is that your object goes out of scope between the
time you store the 'Long' memory address and then use it later -- for
example, when you pass it to your VB6 DLL to be used. If the object has gone
out of scope in the mean time, then you have passed an invalid memory address
-- there is no longer a valid object there because the memory has been
reclaimed. Now how does the caller know that the pointer value is now
invalid? Generally by trying to use it and crashing the system with a
non-recoverable GPF. :-(
Note that this can't happen when passing around a strong reference because
the system will not allow an object to go out of scope so long as there is at
least one, valid strong reference to it. But stuffing the value of the memory
address into a 'Long' integer variable circumvents the system: we know the
memory address, but the system doesn't know that we are holding a reference
to it, so the object *can* go out of scope while we hold this memory address.
We are doing this intentionally, but this does not mean that it isn't
dangerous.
There are some ways of being able to test if the 'Long' pointer value is
valid before using it. Bruce Mckinney has written about an approach (I forget
where) where the 'Long' pointer values to your own classes are stored in a
Collection, Dictionry, Hash Table, or similar data structure when the class
is first created, and then the 'Long' pointer value is are removed from the
collection within the Class Terminate event. Therefore, any code being passed
a weak reference could first test if this value exists in the collection. If
the 'Long' exists it is valid, if not present, then it is not valid.
This doesn't work for classes that you do not control and does not work in a
in a multi-threading environment (because in the time between the time you
check the validity and then use it the weak pointer could become invalid),
but it does make it possible for one's own classes in a single-threaded
environment. Although, one has to be careful of handle re-use: this is where
your 'Long' value is stored in the collection, the object goes out of scope
(so your 'Long' value is now invalid) and then a *new* object is created at
the same memory address location of the previous object and this new 'Long'
value is placed within the collection. You then go to make use of the 'Long'
value, check the collection (it's there!) and then make use of it... but you
are now making use of a *different* object, and the results could be bizarre
and all-but-impossible to debug.
In fact, most problems you hit when trying to work with weak reference in
VB6 or VBA are of the "bizarre and all-but-impossible to debug" variety, so
be warned!
I would not use weak references like this unless you had a real need. A
"real need" would exist only if you either: (a) had a circular reference
problem to fix, or (b) had a complex object for which you wanted to use the
technique called "Resurrection", which is where if your object goes out of
scope and your weak reference becomes invalid, you re-create ("resurrect")
your object on the fly.
But even in these cases I would not go here. It is very tricky and tough to
get these things right. In VBA and VB6 I would simply live with any memory
leaks that might be occurring due to circular reference issues. It's just too
complex to fix this properly. These memory leaks in VBA and VB6 are very
rarely an issue because one does not usually create sprawling object graphs
that take up huge amounts of memory.
If this were really a problem, then I would advocate not trying to cook up a
home-grown system to manage weak references, and instead move on to .NET,
where circular reference issues have been eradicated by using a "trace from
the root" garbage collection pattern instead of a "reference counting"
garbage collection pattern. The downside is that .NET gives up "deterministic
finalization", but the overall tradeoff is very positive.
In short: I think you're instincts were very right: if it ain't broke, don't
fix it.
-- Mike