A common piece of advice is to never mix object references with interface references. What that means is that if you need to instantiate a class and use any of its interfaces, it's best to not refer to it via an object-reference type. You've violated that advice by changing your variables to be of type TMyClass
instead of an interface type. Declare them as interface variables instead; I'd use IUnknown
.
The reason for this advice is that object references are not treated the same as interface references. The compiler always inserts reference-counting code for interface variables, and that code is oblivious to any object references anywhere else in your program. Due to reference counting, an object-reference variable could become invalid after changes to some interface variable, and it's easy to overlook that while writing programs. If you never have an object-reference variable, then you don't need to worry about that possibility; an interface reference should always be valid.
If MyClass1
is an object reference, then you should not call Free
on it after you've assigned it to an interface variable. Here's some of your code, annotated with the object's reference count:
MyClass1 := TMyClass.Create; // initialized to 0
bSupports := Supports(MyClass1,IFirstInterface,i1); // incremented to 1
if bSupports then
begin
Memo1.Lines.Add('MyClass1 supports IFirstInterface');
DoSomethingWith(i1);
end
else
Memo1.Lines.Add('MyClass1 does not support IFirstInterface');
bSupports := Supports(MyClass1,ISecondInterface,i2); // incremented to 2
if bSupports then
begin
Memo1.Lines.Add('MyClass1 supports ISecondInterface');
DoSomethingElseWith(i2);
end
else
Memo1.Lines.Add('MyClass1 does not support ISecondInterface');
MyClass1 := nil; // still 2
i1 := nil; // decremented to 1
i2 := nil; // decremented to 0; the object gets destroyed
If you were to call MyClass1.Free
at any point, your program would crash. Freeing the object yourself would not change the values in i1
or i2
, so the compiler's automatically inserted reference-counting code would still execute. It would attempt to reduce the reference count of an already-freed object, which is obviously not good.
But suppose you waited until after you cleared i1
and i2
, as in this code:
i1 := nil;
i2 := nil;
MyClass1.Free;
That's still wrong. Clearing the variables sets the reference count to 0, so the object gets destroyed upon assigning to i2
; the value in MyClass1
is invalid, so you shouldn't call Free
on it there, either.
The safest thing to do, once you've assigned an object reference to an interface reference, is to clear the object reference immediately. Then you won't be tempted to use it anymore.
There is typically no need to clear an interface variable. It gets cleared automatically at the end of its lifetime (which for local variables is when they go out of scope at the end of the function). Furthermore, if you call Supports
and pass in an already-assigned interface reference, it will either receive an interface reference to the new object, or it will be cleared; it will not continue holding its previous value.
That is, when you call Supports(MyClass2,IFirstInterface,i1);
, there was no need to clear i1
first. The call to Supports
will either fill i1
with a reference to the IFirstInterface
for the object referenced by MyClass2
, or it will store nil
in i1
.