얼마전 작업한 프로젝트 코드의 메모리 관리 부분을 검토하다가 궁금증이 생겼다.
TObject.Free 와 Sysutils.FreeAndNil 의 차이점이 무엇일까.
"Free vs FreeAndNil" 키워드로 구글링을 해보니, stackoverflow 질문 글이 가장 먼저 보였다.
Stackoverflow 질문글 :
https://stackoverflow.com/questions/3159376/which-is-preferable-free-or-freeandnil
Free와 FreeAndNil 을 각각 어떤 상황에서 사용해야 하는지, 둘의 차이점은 무엇인지.
라는 질문인데, 추천을 가장 많이 받고 채택도 받은 답변 내용을 대략적인 영어 해석과 함께 정리해보았다.
1. FreeAndNil 의 구현부.
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
2. 예제 및 설명.
procedure TForm1.FormCreate(Sender: TObject);
var
bm: TBitmap;
begin
bm := TBitmap.Create;
bm.LoadFromFile('C:\Users\User\Desktop\up.bmp');
bm.Free;
if Assigned(bm) then
bm.SaveToFile('C:\Users\User\Desktop\test.bmp')
else
ShowMessage('Cannot save! The bitmap does no longer exist!');
end;
- 위 코드는 바탕화면에 텅 비어있는 bitmap 파일을 생성할 것이다.
1. 파일이 텅 비어있는 이유
- bm 변수를 free 시켜줬기 때문.
2. showmessage를 출력하지 않고, 파일이 생성되는 이유
- 변수 bm 은 여전히 메모리 주소를 point 하고 있는 "assigned" 상태이기 때문에, Assigned(bm) 의 리턴값은 True이다.
- 이와 같은 에러를 피하기 위해서, 안전창치로 bm := nil; 을 작성해넣으면
Assigned(bm)이 false 값을 리턴할 것이다.
- 그래서 FreeAndNil(bm) 은, 아래 두 줄의 코드를 단축시켜 놓은 것이다.
bm.free;
bm := nil;
* 코드의 순서를 바꿔도 런타임 에러는 발생하지 않는다.
정리
위 예시를 통해, 객체를 Free 해주고 난 뒤에 재사용을 하게되면 런타임 에러도 없이 프로그램이 오작동 할 수 있다는것을 깨달았다. 물론 그렇게 사용하는 경우는 대부분 실수일 것이다.
그래서 개발자가 예상하지 못한 에러나 버그가 발생할 경우를 대비해서 Free 보다는 FreeAndNil 을 사용하는것이 좋다고 판단된다.
추가로, 런타임 에러가 발생하는 경우에 대한 테스트를 진행해봤다.
(테스트 환경 : Delphi XE2)
* FreeAndNil 은 여러번 실행해도 런타임 에러가 발생하지 않는다.
procedure TFormFreeAndNil.ButtonExecTwiceClick(Sender: TObject);
var
myList :TList;
begin
myList := TList.Create;
//FreeAndNil myList object.
FreeAndNil(myList);
//Again but safety : it ignores nil objects.
FreeAndNil(myList);
end;
- 프로시져 FreeAndNil 은 "Nil"을 Pointing 하고있는 객체는 무시하도록 예외 처리가 되어있기 때문.
- 따라서, 아래처럼 사용하면 에러가 발생한다.
procedure TFormFreeAndNil.ButtonFreeAndFreeAndNilClick(Sender: TObject);
var
myList :TList;
begin
myList := TList.Create;
//Only "Free" myList object.
myList.Free;
//It create Error "Invalid pointer operation" : FreeAndNil() is ignore "nil objects"
//but, myList is still point [TList]. => Not "nil objects"
FreeAndNil(myList);
//Memo : Execute Twice "myList.Free" is create same error content. (Invalid pointer operation)
end;
- FreeAndNil(myList); 을 실행할 때, "Invalid pointer operation" 에러가 발생한다.
* Free; 만을 사용하는것이 더 좋은 경우는 없나?
- 이건 아직 잘 모르겠다... 자료를 조금 더 찾아봐야 할 듯 ㅜㅜ.
procedure TFormFreeAndNil.ButtonReuseClick(Sender: TObject);
var
myStringList :TStringList;
begin
myStringList := TStringList.Create;
myStringList.Add('Item1');
myStringList.Add('Item2');
myStringList.free;
//Check the object is freed.
if PULONG_PTR(PByte(myStringList) - sizeof(ULONG_PTR))^ and 1 <> 0 then
showmessage('myStringList is Freed.');
myStringList.Add('Item3'); //This line is not create error.
//ReCheck but It is still free
if PULONG_PTR(PByte(myStringList) - sizeof(ULONG_PTR))^ and 1 <> 0 then
showmessage('myStringList is Freed.');
ShowMessage(myStringList[0]); //message : "Item3"
FreeAndNil(myStringList);
myStringList.Add('Item4'); //This line is create Error : "Invalid pointer operation."
end;
이 코드를 보면, 결국 FreeAndNil 을 해주기 전까지는 런타임 에러 없이 프로그램이 동작한다.
하지만 Item1, Item2 를 Add 해주고 Free; 를 해주었기 때문에 데이터가 유실되어있고, 할당되었던 메모리도 해제 되어있음을 확인할 수 있다.
이런 경우가 개발자가 의도하지 않은 경우(버그)로 이어질 가능성이 클 것으로 생각된다.
결론
완전한 메모리 해제와, 버그 예방 차원에서 FreeAndNil 을 사용하는 것이 좋겠다.
하지만, Free; 만을 사용하는것이 더 나은 경우도 분명 있을 것 같은데, 그건 조금 더 찾아봐야 알 것 같다.
'Delphi' 카테고리의 다른 글
[델파이/Delphi] 열거형(Enum) 사용 예시 (0) | 2021.11.01 |
---|---|
[델파이/Delphi] "Interface not supported" 에러 (ExcelExport) (0) | 2021.10.26 |
[Delphi/델파이] 최근 에러 조회하기 - GetLastError (0) | 2021.10.15 |
[Delphi/델파이] Theme Editor (테마 에디터) 소개 (0) | 2021.07.04 |
[델파이/Delphi] Application.ProcessMessages 사용시 주의점 (0) | 2020.07.26 |