• Welcome to Theos PowerBasic Museum 2017.

Object Lifetime and Scope

Started by Eriq VanBibber, April 07, 2009, 09:57:56 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Eriq VanBibber

Does anyone know whether we need to call object.Release for a Direct COM object in PB9?

Documentation does not say either way only that setting an object to NOTHING releases memory and resources.


Edwin Knoppert

#1
A variable holds the interface and will call .release() for you.
The variable will be set to empty or nothing (read: invalid pointer)
When all variables holding the same object instance are released, the data and internal allocation will be cleared.

When the variable goes out of scope or when set to nothing it will call release, never call it yourself unless you are 'misusing' the object's interface.
I do that in some cases, for example to hold the interface in a long integer.
When a long is cleared, the release() will not called of course and so i have to make sure it does.



Eriq VanBibber

Sweet!

I figured as much and have been programming it that way, but without knowing for sure.

Y, i have some cases where i have aggregated COM objects and have to manage the addref and release myself...

Thanks!

Edwin Knoppert

Oh btw, to be clear, i was talking about variant, dispatch and interface for variables but you seem to understand that :)
These 3 are supported by PB to be released (or manually) if they contain a com interface.

Eriq VanBibber

Ok, new question.  Does PB9 automatically call release on INSTANCE variables in a CLASS?

Considering the example class below...when an instance of the class "test" is destroyed or goes out of scope, what happens to the instance variable 'var1'?  Is PB calling relase on all internal object references before releasing the class itself? Or do we need to do release/NOTHING in a destructor?

CLASS test
    INSTANCE var1 AS ISomething

    INTERFACE ITest
        INHERIT IUNKNOWN

        METHOD t()
            var1 = CLASS "Something"
        END METHOD
    END INTERFACE
END CLASS

Edwin Knoppert

PB releases all stuff for you but this discussion has been on the pb forum as well.
A logical error is made really quick.
For example my main interface held a reference to a 'child' item interface and this child also to the main.
A circular reference that is and is not going to be released (chicken - egg).

Your question can be solved by yourself by using an OutputDebugString() in the release call.
This is how i develop my code to be sure it is released as anticipated.


José Roca

Quote
Considering the example class below...when an instance of the class "test" is destroyed or goes out of scope, what happens to the instance variable 'var1'?  Is PB calling relase on all internal object references before releasing the class itself?

Yes, it releases the instance variables.

Eriq VanBibber

Ok, another question then...

When working in loops, i have a variable that i instantiate inside the loop, and set it to nothing just before the next iteration.

I'm concerned that this may not be safe since when the function completes, PB may try to call release on an object that already has a ref count of zero.  Am i overly concerned?

Consider the following function:

FUNCTION myLoop(obj as ISomeObject)

  DIM x AS INTEGER

  FOR x = 0 TO obj.Count
    DIM subObj as ISubObject
    subObj = obj.item(x)
    ...
    {some work is done on subObj}
    ...
    subObj = NOTHING '// need to explicitly free the object to avoid a memory/handle leak
  NEXT

END FUNCTION

Will PB already know that subObj has a ref count of zero and not call release?
OR
Will PB call release anyway and swallow any error about the ref count being at or less than zero?

Regards,
Eriq

José Roca

#8
Quote
Am i overly concerned?

Yes, you are :)

Quote
Will PB already know that subObj has a ref count of zero and not call release?
OR
Will PB call release anyway and swallow any error about the ref count being at or less than zero?

PB can't know if the reference count is zero, but it doesn't need it. After setting subObj = NOTHING, the pointer hold by that variable is 0, and PB is clever enough to know that calling Release with a NULL pointer will cause a GPF, therefore it does nothing.

BTW even subObj = NOTHING in the loop isn't needed, because PB will call Release before assigning a new value to it (unless the pointer is already 0). In fact, you don't ever need to set an object variable to Nothing unless you want release objects in an orderly manner.

PB's object variables are smart pointers. Only if you manipulate them in an unsupported way, like poking a value (something I do often, but I know what I'm doing), may you need to be concerned about AddRef and Release.

Eriq VanBibber

Very nice.  Thanks much.

I'm a MAPI programmer and always have to be concerned about such.

-Eriq

Eriq VanBibber

Ok, i thought i had this all figured out...

I'm getting a memory access violation when my object's lifetime expires.

I know this because the debugger runs fine until is step out of my function.
I placed brakepoints in DESTROY for my class, and it bombs after leaving the DESTROY function.

So, what do we need to know about passing objects in to other object's methods?
Does PB call addref every time?  Could there be times where passing an object reference doesn't trigger addref?

If i figure it out before you do, i'll update this thread.

-Eriq

José Roca

#11
Quote
Does PB call addref every time?

Every time you pass it by value.

Quote
Could there be times where passing an object reference doesn't trigger addref?

If you pass it by reference.

Do not set an object variable passed by reference to Nothing unless you first have called AddRef.

Edwin Knoppert

>Do not set an object variable passed by reference to Nothing unless you first have called AddRef.
Huh?
I set this to nothing when passed byref, it releases the variable just fine.
I often do this to reuse an existing var and a previously set object must be released of course.
?

José Roca

 
If you pass an object variable by reference, the reference count isn't incremented, therefore you must not set it to NOTHING.


#COMPILE EXE
#DIM ALL
#INCLUDE "windows.inc"
#INCLUDE "REGEXP.INC"

SUB Foo (BYREF pRegExp AS IRegExp)
   pRegExp = NOTHING   ' <-- don't do this
END SUB

FUNCTION PBMAIN () AS LONG

   LOCAL pRegExp AS IRegExp
   pRegExp = NEWCOM "VBScript.RegExp"
   Foo pRegExp
   ' Now pRegExp is a null reference
   ' If you try to use it, it will GPF

END FUNCTION


Edwin Knoppert

#14
I think we have a miscommunication, i do this all the time like:


Class Class1

    Class Method Destroy
        MsgBox "D"
    End Method

    Interface Interface1: Inherit IUnknown

        Property Get DateValue() As String
            Property = "11"
        End Property

    End Interface

End Class

Function CreateTheObject( ByRef o As Interface1 ) As Long
    o = Class "Class1"
End Function


Called like:

    Local c As Interface1   
    c = Class "Class1"   
    CreateTheObject( c )


Passing c means the address of c and therefore the same principles apply as using code in the original procedure.
The msgbox is executed twice.. as should.

Your example isn't wrong just that you say don't do this.. i guess you mean don't do this if you suspect the var passed should still have an object.
Of course..