Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

how do you free a function result? 5

Status
Not open for further replies.

DelphiAaron

Programmer
Jul 4, 2002
826
0
16
AU
i have this function that takes a jpg and returns a bmp but the result bitmap is not freed unless theres an exception, and of course you cant free it here otherwise the result is not returned. ??

Code:
function MakeBmp(Source : TGraphic): TBitmap;
begin
 Result := TBitmap.create;
 Try
   with Result do
    begin
     Width := Source.Width;
     Height := Source.Height;
     Canvas.Draw(0,0,Source);
    end;
  except
    Result.Free;
    raise;
  end;
end;

Aaron Taylor
John Mutch Electronics
 
make something like this :

Code:
function MakeBmp(Source : TGraphic; var Dest : TBitmap) : boolean;
begin
 Result:=True;
 Try
   with Dest do
    begin
     Width := Source.Width;
     Height := Source.Height;
     Canvas.Draw(0,0,Source);
    end;
  except
   Result:=False;
  end;
end;

call function like this

Bmp:=Tbitmap.Create;
if MakeBmp(Src, Bmp) then
 begin
  DoSomethingWitBmp;
 end;
FreeAndNil(Bmp);

off course other variations are possible, try to adapt the function in a way it is the most conveniant for your app...

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
In your except block you could use FreeAndNil(Result) rather than just Result.Free. That way if an error occurs in the function it will return nil. You could then call it something like this:

Code:
Bmp := MakeBmp(Src);
if assigned(Bmp) then
begin
  DoSomethingWithBmp;
end;
FreeAndNil(Bmp);

It's not a significant improvement on whosrdaddy's solution, it just means that you don't have to explicitly create the bitmap before calling the function each time
 
All good advice above.

With methods that create objects, it's good practice to label them as such. eg.
Code:
function CreateBitMap: TBitMap;
begin
  Result := TBitMap.Create;
  // snip
end;

var
  b : TBitMap;
begin
  b := CreateBitMap;
  try
    if Assigned(b) then
    begin
      // snip
    end;
  finally
    b.Free;  // doesn't matter if b = nil;
  end;
end;
That way, your main code can keep the structuring as normal as possible.
 
Before jumping straight in (after what has already been said) I had a dig around, because my first thought was.. Why free this at all? Surely local variables are stored on the stack and so will not persist when the function returns.

I wondered if this might not be the case with structured types (Objects).
The following text suggests these types of things are stored in heap memory (and so must be freed).
But it also talks about automatic deallocation so that is not clear.
Having said that, explicitly Freeing Objects is always the best practise.

This from here

Dynamic variables, long strings, wide strings, dynamic arrays, variants, and interfaces in Delphi are all variable types that are stored in heap memory. In order to use them, you use a reference variable, i.e. a pointer to the actual variable itself. In Pascal, this reference can be explicit by the use of a reference variable or implicit, the latter for example by using a with statement. In assembler, you must make sure you store the pointer to the variable memory somewhere in order to be able to access it.

Consequently, if you use heap allocated types as local variables, memory will be allocated for the reference (the pointer) to that variable, but you are responsible for the actual allocation and deallocation of the memory. In Pascal, most of these types are automatically managed, so allocation and deallocation happens behind the scenes. In assembler, you obviously are responsible yourself for taking care of that.



Steve: Delphi a feersum engin indeed.
 
self created Heap objects are not automatically freed. I can assure that. if you don't want cope with the hassle of freeing objects, use interfaces instead, those are freed automatically when they are out of scope.

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
One heap-allocated type that is automatically managed by Delphi is AnsiString. In Delphi6? with the $X+ (long strings) compiler option declared, the String type changes from ShortString (maximum length of 255 characters) to AnsiString (limited only by available memory). Despite AnsiString requiring memory from the heap, Delphi manages the allocation and deallocation for you, so that there's no change required to your code.

I'm not positive, but I'd guess that the Variant type would use the heap as well?

whosrdaddy, could you give an example of using an interface as you suggested? I still struggle a bit with that concept. I know that an interface can be considered a class with only abstract methods, so I'm not sure how it can help here.
 
Griffyn, here's a nice example :

Code:
Problem: Memory Leaks 
Memory Leaks are an insidious bug, silently leaking away until your application chokes to death. While the Object Pascal language provides us with plenty of ammunition in our fight against memory leaks, a surprising number of even experienced Delphi developers either aren't using them correctly or simply aren't aware of them. 

Owners, Try..Finally blocks and Free/FreeAndNil are all useful tools, however that was not the problem with this particular project. All of the developers on the team were either already aware of these things or were quickly made aware. But this didn't seem to reduce our leak problems. What we needed was a no-brainer solution that would require our developers never to have to worry about cleaning up after themselves. Now, a wholesale move to Java with it's built in Garbage Collection was out of the question, so we decided to build some of our own. 

Solution: Interfaces 
What I'm about to present is not a Garbage Collection system for Delphi, but the use of an existing feature (ie. Interfaces) to perform a similar function.  

Cleaners 
One situation where the developers were getting themselves in trouble was in simply not always remembering to free an object. While at first glance this may seem like a trivial problem to solve (can anyone say try..finally), for whatever reason, these guys just couldn't get it. So rather than continue to bash our head against a wall, we decided to make it so they didn't have to. 

First off, we declared a marker interface (an interface without methods) like this: 

ImtCleaner = interface 
['{34E82AD7-0DC1-42AE-B484-0E233F25E858}'] 
end; 


Having done this, we declared an object that implements this interface like this: 

TmtCleaner = class(TInterfacedObject, ImtCleaner) 
private 
  FObject : TObject; 
public 
  constructor Create(AObject : TObject); 
  destructor Destroy; override; 
end; 


The implementation of the constructor and the destructor look like this: 

constructor TmtCleaner.Create(AObject: TObject); 
begin 
  FObject := AObject; 
end; 

destructor TmtCleaner.Destroy; 
begin 
  FreeAndNil(FObject); 
  inherited; 
end;

I copied the code from here :


also look into the TRestore component on that site, it's a nice example how to make clean and powerfull code through the usage of interfaces...

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
That makes sense. I like the TRestore component a lot. I haven't delved too heavily into projects that would benefit from it, but it's good to tuck that into my brain for future use.

thanks whosrdaddy!
 
whosrdaddy, I've just read through the excellent article you linked to in your post - many thanks. Our development team will definitely be discussing some of the items raised. I'm particularly interested in the use of interfaces, as I haven't really had much experience of them in Delphi.

Clive
Runner_1Revised.gif

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer." (Paul Ehrlich)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
Agree a good article. some same niggles..

1. Grey text on a black background!! I found myself having to drag a highlight over to make it readable. So
Delphi 10
Web design 0.

2. with statements
He's really taking about nesting 'with' statements, which as he points out can cause lots of problems.
But for a single items, then I would say it has uses. e.g setting up multiple parameters on a modal form.

3. Code insight. That's a free tool is it?



Steve: Delphi a feersum engin indeed.
 
sggaunt, Code insight is part of the Delphi IDE. Have a look under Tools, Editor Options, then click the Code Insight tab. That's where it is in Delphi 6 anyway.

I agree with you on with statements, and I never nest mine. I really only ever use them in this situation:
Code:
with TRegistry.Create do
  try
    RootKey := HKEY_LOCAL_MACHINE;
    //snip
  finally
    Free;
  end;
 
Hi aaronjme,

It did cross my mind that could be the case, but I figured no harm in pointing it out. You never know - somebody else who's Code Insight has been turned off for whatever reason might go 'huh? wow!' [smile]
 
while were talking Code Insight, mine comes up with an error saying
[Pascal Error] MainU.pas(1): Unable to invoke Code Completion due to errors in source code

anyone know where to start looking. ???

Aaron
John Mutch Electronics
 
Code Completion can only work if all the code between your class declaration and the position it would add the automatic code can be successfully compiled. If not, then you will receive this error message.
 
Code insight. Sarcasm Well Yes and No.
When he mentioned Code insight, I thought he meant something else.

A little confusion because I think he actually meant 'code completion' which is on another tab of the editor options.

Not sure with the premise anyway? Because by the time you have set up enough 'instances of long references', to be of any use, then you could probably have typed them in anyway.

As far as Delphi's Code insight is concerned who would ignore the information provided? and it is on by default.

Also it doesnt work if you are examining code whilst the debugger is running the application. which is understandable I suppose!



Steve: Delphi a feersum engin indeed.
 
just define a plainold variable for the result first.

var Ans: TBitMap;


As yo've noticed the "result" keyword is problematic.

The at the very end:


Result := Ans;




 
grg999

I assume you mean to then free Ans?
Like this but it doesn't work it just hangs up. I think this is because within the function, R is still the result value.

Code:
function TUpdateScan.ExtractQVIs(S: TStringList): TStringList;
var P: integer;
    A: integer;
    R: TstringList;
begin
   R := TStringlist.Create;
   for a:= 0 to S.count - 1 do
      begin
         p := pos('qvi', S.Strings[a]);
         if p > 0 then
               R.Add(S.Strings[a]);
      end;
  Result := R;
  Freeandnil(R);
end;

I am not total convinced by 'niceness' of the Global solution has anyone any thoughts on this instead..
Still have to create the temporary Stringlist externaly

Code:
procedure TUpdateScan.ExtractQVIs(S: TStringList; var R: Tstringlist);
var P: integer;
    A: integer;
begin
   R.clear;
   for a:= 0 to S.count - 1 do
      begin
         p := pos('qvi', S.Strings[a]);
         if p > 0 then
               R.Add(S.Strings[a]);
      end;
end;



Steve: Delphi a feersum engin indeed.
 
sggaunt, just to clarify your first example:
Code:
[b]var[/b]
  R: TstringList;
[b]begin[/b]
  R := TStringlist.Create;
  [navy]// snip[/navy]
  Result := R; [navy]// Result and R now point to the same object[/navy]
  Freeandnil(R);  [navy]// R now equals [b]nil[/b][/navy]
  [navy]{ The object has been freed, therefore every pointer to that object (in this case, Result is the only pointer) is now invalid }[/navy]
[b]end;[/b]

I disagree with grg999's assertion that
grg999 said:
the "result" keyword is problematic.
Consider the following methods:
Code:
[b]procedure[/b] GetVarA([b]var[/b] Result: String);
[b]function[/b] GetVarB: String;

Functionally, these are exactly the same, and regardless of [tt]Result[/tt]'s type, they will work as expected.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top