PDA

View Full Version : Free کردن یک فرم بعد از بستن آن



masoode
چهارشنبه 29 اردیبهشت 1395, 18:08 عصر
سلام
من در جایی از برنامه یک فرم را به صورت داینامیک میسازم. می خوام موقع بستن فرم (با استفاده از کلید "ضربدر" بالای فرم) حافظه اختصاص یافته را نیز پاک کنم


procedure TfrmLargView.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Free;
end;

این کار را کردم اما نشد!!!

Mahmood_M
چهارشنبه 29 اردیبهشت 1395, 18:26 عصر
Action := caFree;

hadisalahi2
چهارشنبه 29 اردیبهشت 1395, 18:26 عصر
Action := caFree;

masoode
چهارشنبه 29 اردیبهشت 1395, 18:42 عصر
CaFree جواب نمی ده
من در جایی از برنامه این خط را نوشته ام:


if not Assigned(frmLarg) then begin
frmLarg:=TfrmLargView.Create(Self);
.
.
.
.
.
end;


یک بار هم به جای Assigned از frmLarg=nil استفاده کردم. اما اگر برای اولین بار که هنوز فرم ایجاد نشده به این خط برنامه برسد به درستی فرم را می سازد و دفعات بعدی با توجه به اینکه فرم وجود دارد از این قسمت صرف نظر میکند. مشکل آنجایی بوجود می آید که فرم دوم با استفاده از "ضربدر" بسته میشود (در onClose خطی که شما فرموده اید را نوشته ام) این با رسیدن به دستور Assigned یا frmLarg=nil هنوز فکر میکند که فرم وجود دارد!!!
چه کنم؟

Felony
چهارشنبه 29 اردیبهشت 1395, 21:18 عصر
از FreeAndNil استفاده کنید .

masoode
پنج شنبه 30 اردیبهشت 1395, 11:39 صبح
من برنامه ای خیلی ساده نوشته ام برای حل مشکلم. تمام اضافات را حذف کردم اما هنوز مشکلم به قوت خودش باقیه!
140492
میشه برام اصلاحش کنید.
فعلا می خوام با زدن کلید فرم ساخته بشه و اگر دوباره زده شد اگر فرم وجود داشت آن را free کند. فعلا همین!

Felony
پنج شنبه 30 اردیبهشت 1395, 17:13 عصر
با اضافه کردن اون caFree شما در حقیقت فضای اختصاصی به فرمتون رو آزاد کردید .
اشیاء در دلفی به صورت اشاره گر هستند ، وقتی فضای اختصاصی به فرمتون آزاد میشه هنوز اشاره گر مربوطه به مکانی از حافظه اشاره میکنه که بعد از آزاد سازی حافظه دیگه valid نیست ، باید بعد از آزاد سازی فرم آدرس اشاره گر رو هم از بین ببرید :


if not Assigned(frm) then
begin
frm := TForm2.Create(Self);
frm.Show;
end
else
frm := nil;


یا به جای caFree و nil کردن اشاره گر از FreeAndNil که در ئست قبل گفتم استفاده کنید ، این تابع هم فضای اختصاصی شی رو آزاد میکنه و هم اشاره گر مربوطه رو Nil میکنه .

Mahmood_M
پنج شنبه 30 اردیبهشت 1395, 19:26 عصر
توضیحات داده شد، اما برای بررسی در حال نمایش بودن یا نبودن فرم، به جای چک کردن مقدار frm ( که به فضای فرم اشاره می کنه ) بهتره مثلا یک متغیر دیگه درنظر بگیرید و در رویداد OnShow و OnClose فرم دوم ، اون رو مقداردهی کنید ، مثال :


var
Form1: TForm1;
Count:Word;
frm:TForm2;
FrmShowing : Boolean = False;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
if FrmShowing = False then
begin
frm := TForm2.Create(nil);
frm.Show;
end
end;

...

var
Form2: TForm2;

implementation

{$R *.dfm}

uses Unit1;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;

FrmShowing := False;
end;

procedure TForm2.FormShow(Sender: TObject);
begin
FrmShowing := True;
end;

* نیازی به Nil کردن frm نیست

یا اینکه از ShowModal استفاده کنید :


procedure TForm1.Button1Click(Sender: TObject);
begin
frm := TForm2.Create(nil);
try
frm.ShowModal;
finally
frm.Free;
frm := nil;

// FreeAndNil(frm);
end;
end;

* نیازی به Action = caFree نیست

masoode
سه شنبه 04 خرداد 1395, 10:59 صبح
آیا FreeAndNil را در رویداد onClose از فرم دوم هم میشه استفاده کرد؟ جایی که Action := caFree; دستور نوشته شده؟

Mahmood_M
سه شنبه 04 خرداد 1395, 11:49 صبح
استفاده از FreeAndNil برای متغیر frm هستش، با این دستور فضایی که frm بهش اشاره میکنه آزاد میشه ( فرم Free میشه ) و خود frm هم برابر با nil قرار می گیره
دلیلی برای استفاده از FreeAndNil در رویداد OnClose وجود نداره
بهتره از همون روشهایی که اشاره شد استفاده کنید و برای یک شرط ساده درگیر مباحثی مثل اشاره گر ها و Free کردن و ... نشید

meysam_212
سه شنبه 04 خرداد 1395, 23:11 عصر
دقت کنید که اگه از cafree استفاده کنید درواقع فرم حذف شده و شما اگه بخواین به formshowing دسترسی داشته باشین خطا برمیگردون مگه اینکه formshowing رو جای دیگه ای تعریف کرده باشین که جالب نیست، در حالت شو مدال خوب مشکلی نیست ولی برای اینکه تو onclose استفاده کنین و برنامرو درگیر شو مدال و کدهای تکراری نکنین میتونین تو onclose از
;Action:=cafree
;Self =nil
استفده کنین که خودم اینکارو کردم و مشکلی هم نداشتم اما بنظر من اگه بزاری کاربر هر چندتا نمونه از فرم بتونه بسازه جالب میشهنمیدونم منظورم و درست رسوندم یا نه ولی نمونه میزارم



procedure TForm1.Button1Click(Sender: TObject);
begin

With TForm2.Create(nil) do
Begin
Show;
{میتونی اینجا از onclose استفاه کنی}
onclose:= self.myOnClose;
End;

;End

Mahmood_M
سه شنبه 04 خرداد 1395, 23:34 عصر
دقت کنید که اگه از cafree استفاده کنید درواقع فرم حذف شده و شما اگه بخواین به formshowing دسترسی داشته باشین خطا برمیگردون مگه اینکه formshowing رو جای دیگه ای تعریف کرده باشین که جالب نیست، در حالت شو مدال خوب مشکلی نیست ولی برای اینکه تو onclose استفاده کنین و برنامرو درگیر شو مدال و کدهای تکراری نکنین میتونین تو onclose از
;Action:=cafree
;Self =nil
استفده کنین که خودم اینکارو کردم و مشکلی هم نداشتم اما بنظر من اگه بزاری کاربر هر چندتا نمونه از فرم بتونه بسازه جالب میشهنمیدونم منظورم و درست رسوندم یا نه ولی نمونه میزارم
frmShowing در Unit1 تعریف شده نه در Unit2 که با حذف فرم قابل دسترسی نباشه
با Free شدن فرم متغیرهای موجود در Unit اون فرم از دسترس خارج نمیشن و اصلا ارتباطی با هم ندارن !

Self =nil چه کاری انجام میده ؟! ، Self شما رو به Form فعلی میرسونه نه متغیر frm



procedure TForm1.Button1Click(Sender: TObject);
begin

With TForm2.Create(nil) do
Begin
Show;
{میتونی اینجا از onclose استفاه کنی}
onclose:= self.myOnClose;
End;

;End
self.myOnClose کجا تعریف شده و چه کاری انجام میده ؟

یوسف زالی
چهارشنبه 05 خرداد 1395, 00:29 صبح
برای شو بودن یا نبودن فرم، showing وجود داره و اصلا برای همین کار ساخته شده.
گرفتن یک متغیر کار درستی نیست.
اگر با این راهها به جواب نرسیدید، می تونید هندل فرم رو با API های مربوطه چک کنید، که خب البته جزو راههای آخره.

Mahmood_M
چهارشنبه 05 خرداد 1395, 00:47 صبح
برای شو بودن یا نبودن فرم، showing وجود داره و اصلا برای همین کار ساخته شده.
فرم Free شده، خاصیت Showing وجود نداره

گرفتن یک متغیر کار درستی نیست
چرا درست نیست ؟!

اگر با این راهها به جواب نرسیدید، می تونید هندل فرم رو با API های مربوطه چک کنید، که خب البته جزو راههای آخره
یک مثال بزنید لطفا، فرم Free شده، چطور چک کنیم ؟ چی رو چک کنیم ؟
مقدار هندل رو ذخیره کنیم ؟ یعنی متغیری برای ذخیره هندل در نظر بگیریم ؟

به هر حال، جواب کامل داده شده ...

meysam_212
پنج شنبه 06 خرداد 1395, 09:20 صبح
frmShowing در Unit1 تعریف شده نه در Unit2 که با حذف فرم قابل دسترسی نباشه
با Free شدن فرم متغیرهای موجود در Unit اون فرم از دسترس خارج نمیشن و اصلا ارتباطی با هم ندارن !


Self =nil چه کاری انجام میده ؟! ، Self شما رو به Form فعلی میرسونه نه متغیر frm




self.myOnClose کجا تعریف شده و چه کاری انجام میده ؟






بنظر من استفاده از یه متغیر داخل یونیت1 کار خوبی نیست، درواقع با این کار شی گرایی رعایت نشده. بهترین راهکار اینه که عملیات آزاد شدن داخل کلاس خودش باشه، مثلا تو TForm2.OnClose
که برای اینکار من یه روشی رو قبلا استفاده کردم و جواب داده که پایین میزام، منظور از self := nil برای زمانی بود که بخواییم داخل خود کلاس مورد نظر آزادش کنیم یعنی تو TForm2 که نمونش تو کد زیر هست


فرض کنیم میخوایم از داخل unit1 کلاس TForm2 رو از unit2 اجرا کنیم و بدون ShowModal کردن اون آزادش کنیم تو زمان بسته شدن فرم

{یه روش اینه که داخل TForm2.OnClose این رو بزاریم}


(...)TForm2.OnClose
begin
{دقت کنین که Self:= Nil باید بعد از Action:= Cafree باشه}
Action := CaFree;
Self := nil;
end;




{حالا برای آبجکت ساختن از داخل کلاس TForm1}
TForm1.Button1OnClick(...)
var f: TForm2;
begin
{یه روش اینه که از متغییر محلی استفاده کنیم، مثل این کد}
f:= TForm2.Create(nil);
f.Show;


{یه روش هم بدون نیاز به متغییر محلی، مثل این کد}
With TForm2.Create(nil) do
begin
show;
end;
end;




{در مورد self.onclose ما میتونیم یه پروسیجر بسازیم و پارامتر های رو توش تعریف کنیم که پروسیجر های onClose فرم ها دارن، بعد میتوین onClose هر فرمی رو برابر onclose خودمون که یه پروسیجر قرار بدیم و مهم هم نیست که این پروسیجر رو کجا تعریف کرده باشیم، یه مثال میزارم }


{تعریف MyOnCloseTForm}
TForm1.MyOnCloseTForm
begin
Action:= Cafree;
Sender:= nil;
end;




{استفاده از MyOnCloseTForm}
TForm1.ButtonOnClick
var f:TForm2;
begin
f:= TForm2.Create(nil);
{حالا میتونیم MyOnCloseTForm فرم ساخته شده رو استفاده کنیم}
f.OnClose:= MyOnCloseTForm;
f.show;




{یا اینکه اگه نخوایم متغییر محلی داشته باشیم، مثل این کد}
With TForm2.Create(nil) do
begin
{دلیل استفاده از self اینه که اگه داخل کلاس TForm2 پروسیجری همنام با MyOnCloseTForm وجود داشت باشه، کامپایلر اشتباه نکنه و از MyOnCloseTForm تعریف شده داخل کلاس TForm1 استفاده کنه}
Onclose:= Self.OnMyCloseTForm;
show;
end;



end;

Mahmood_M
پنج شنبه 06 خرداد 1395, 13:09 عصر
منظور از self := nil برای زمانی بود که بخواییم داخل خود کلاس مورد نظر آزادش کنیم یعنی تو TForm2 که نمونش تو کد زیر هست
نکته اول اینکه Self به Form اشاره می کنه، نه به متغیر ما
نکته دوم اینکه با برابر nil قرار دادن یک اشاره گر، اون رو Free نمی کنید، اصولا یک اشاره گر Free نمیشه، بلکه مکانی که اشاره گر به اون اشاره می کنه Free میشه، نه خود اشاره گر
self = nil هیچ کاری انجام نمیده و چیزی رو آزاد نمی کنه و ارتباطی به آزاد کردن کلاس نداره، Free کردن با برابر nil قرار دادن تفاوت داره



TForm1.MyOnCloseTForm
begin
Action:= Cafree;
Sender:= nil;
end;

Sender = nil هم به همین ترتیب، Sender اینجا متغیر frm نیست، بلکه خود فرم هستش، تفاوتی با Self = nil نمی کنه ، چون اجرا کننده ی Event هم همون Self یا همون فرم هست

بنظر من استفاده از یه متغیر داخل یونیت1 کار خوبی نیست، درواقع با این کار شی گرایی رعایت نشده. بهترین راهکار اینه که عملیات آزاد شدن داخل کلاس خودش باشه، مثلا تو
TForm2.OnClose
مشکلی در آزاد کردن کلاس وجود نداره، کلاس فرم که با Action = Free آزاد میشه
مشکل اینجاست که متغیر frm که اشاره گر به کلاس هست باید nil بشه تا تابع Assigned وقتی اون رو بررسی میکنه، مقدار False برگردونه
از داخل کلاس، شما دسترسی به اشاره گری که به کلاس ساخته شده اشاره می کنه ندارید
استفاده از متغیر Global هم ارتباطی به شی گرایی نداره !

اینکه دوستان سعی می کنند در بحثها شرکت کنند چیز مثبتیه، اما اینکه هر چیزی که به ذهنمون رسید رو با اطمینان بیان کنیم و در مورد درست بودنش تحقیق نکرده باشیم، ممکنه سایر دوستانی که در ابتدای راه یادگیری هستند رو به مسیر اشتباهی ببره
به هر حال ...

راه های زیادی وجود داره، استفاده از متغیر عمومی هم راه ساده ایه که شما رو درگیر بحث اشاره گرها نکنه تا اشتباهاتی مثل کدهای درون پستهای بالا رخ نده
مثلا راه دیگه اینه که شما فرمهای Screen رو چک کنید و وجود Form2 رو بررسی کنید، مثال :


procedure TForm1.Button1Click(Sender: TObject);
var
I : Integer;
FormShowing : Boolean;
begin
FormShowing := False;
for I := 0 to Screen.FormCount - 1 do
begin
if Screen.Forms[I].ClassName = 'TForm2' then
begin
FormShowing := True;
Break;
end;
end;

if not FormShowing then
begin
frm := TForm2.Create(nil);
frm.Show;

frm := nil;
end
end;

...

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;


امیدوارم این بحث ادامه پیدا نکنه
موفق باشید

meysam_212
پنج شنبه 06 خرداد 1395, 18:32 عصر
این مشکل رو سیشارپ هم داره اینجا هم دقت کرده باشین توی کلاس tform2 اول action = cafree شده بعد nil شده، frm هم متغیری از tform2 هست، متوجه نمیشم منظورتون رو ولی اون نیل کردن دقیقا هدفش نیل کردن frm هست.
شاید از رویداد destroy بشه استفاده کرد

اگه بازم frm نيل نشه ميشه كانستراكتر ساخت و frm رو بهش پاس داد
مثل



FCaller: TForm2;
Constructer create(..., Caller: TForm2)
Begin
Inherited create(Owner)
FCaller:= Caller;
End;



TForm2.OnClose
Begin
Action:=cafree;
FCaller:= nil;
End

Mahmood_M
پنج شنبه 06 خرداد 1395, 19:57 عصر
اون نیل کردن دقیقا هدفش نیل کردن frm هست
دوست عزیز، چرا روی یک اشتباه تاکید می کنید ؟!
خوب تست کنید، ببینید بعد از self = nil متغیر frm هم برابر nil میشه یا نه !، کاملا مشخصه که نخواهد شد، اصلا Self ارتباطی با frm نداره
قضیه کاملا مشخصه و توضیحات هم کاملا شفاف بود، قبل از تاکید بی مورد حداقل تست کنید

در کل اگه نتونیم داخل خود کلاس عملیات free و نیل انجام بدیم درواقع ایراد کلاس Form دلفی و سیشارپ هست از نظر من بهتره کلاس رو خودمون توسعه بدیم و استفاده کنیم
مشکل اینجاست که شما تفاوت Free شدن و برابر nil قرار دادن رو نمی دونید و اشاره گری که به فضای فرم اشاره میکنه رو با خود فرم اشتباه گرفتید، frm کلاس فرم نیست، بلکه یک اشاره گر به فضای درنظر گرفته برای فرم محسوب میشه
فرم Free میشه و مشکلی وجود نداره، از داخل خود کلاس، اون رو Free کردیم، اما آدرس فضایی که Free شده هنوز توی frm قرار داره، frm هنوز مقدار داره، آدرس فضای فرمی که Free شده، خود آدرس یک مقداره که درون frm وجود داره
لطفا قبل از ادامه این بحث، در مورد اشاره گرها و متغیرها و کلاس ها و شی گرایی مطالعه کنید
توضیحات کاملا شفاف بود، احساس می کنم اصلا نمی خونید پست ها رو

منظور خاصی ندارم ولی استفاده از متغیر تو فورم۱ جواب میده ولی راه کار جالبی نیست
خوب "جالب" یعنی چی دوست عزیز ؟!
راه های زیادی وجود داره، اگه شما از روش متغیر عمومی خوشتون نمیاد، خوب راه دیگه ای رو امتحان کنید، مثلا فرمهای Screen رو چک کنید ( که در پست قبلی قرار دادم )

شاید از رویداد destroy بشه استفاده کرد
مشکل در رویدادها نیست اصلا
پستها رو دوباره بخونید، به اشتباهاتتون پی خواهید برد

فکر می کنم کاربر سئوال کننده جوابش رو گرفته باشه
لطفا این بحث رو ادامه ندید

meysam_212
جمعه 07 خرداد 1395, 00:30 صبح
شما گفتین اشاره گره و نیل نمیشه. فکر کنم گفتم اگه نیل نشه میتونیم از کانستراکتور استفاده کنیم. صحبت من برای راه کاره بهتره، مثلا اگه کانستراکتور جواب بده بد نیست. بجای استفاده از متغییر تو یه کلاس دیگه چرا تو همون کلاس اینکارو نکتیم وقتی نیاز برنامه نیل شدن متغیر همراه با فری هست. فرض کنیم از متغیر استفاده کنیم و توی یه فرم بخایم ۵۰ تا فرم دیگرو باز کنیم... اسم متغییرا، تعداد و ...
بد هم نیست ادامه بدیم تا یه راهکار نهایی استفاده کنیم هممون، که توسعه دهندرو اتوماتیک از خطا دور کنه، من خودم اینجا به کنستراکتور رسیدم فکر کنم اگه جواب بده خوبه هرچند بازم باید حتما متغییر رو پاس بدیم که میتونیم overload کنیم کانستراکتور رو.

Mahmood_M
جمعه 07 خرداد 1395, 02:12 صبح
با جستجو برای یک راه دیگه موافقم اما اصرار روی موارد بالا بحث رو به جایی نمی رسونه

بد هم نیست ادامه بدیم تا یه راهکار نهایی استفاده کنیم هممون، که توسعه دهندرو اتوماتیک از خطا دور کنه، من خودم اینجا به کنستراکتور رسیدم فکر کنم اگه جواب بده خوبه هرچند بازم باید حتما متغییر رو پاس بدیم که میتونیم overload کنیم کانستراکتور رو.
این میشه یک راه دیگه که البته کمی کار داره
مثلا به صورت زیر :
بازنویسی کلاس TForm2 :


type
TForm2 = class(TForm)
Label1: TLabel;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
FrmVar : ^TForm;
{ Private declarations }
public
constructor Create(AOwner : TComponent; var MyFrm : TForm); reintroduce;
end;

var
Form2: TForm2;

implementation

{$R *.dfm}

constructor TForm2.Create(AOwner : TComponent; var MyFrm : TForm);
begin
inherited Create(AOwner);

FrmVar := @MyFrm;
end;


procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FrmVar^ := nil;

Action := caFree;
end;


ساخت و نمایش فرم :


procedure TForm1.Button1Click(Sender: TObject);
begin
if not Assigned(frm) then
begin
frm := TForm2.Create(nil, TForm(frm));
frm.Show;
end
end;


Create رو بازنویسی کردیم و به صورت ارجاعی متغیر اشاره گر به فرم رو گرفتیم ( MyFrm ) و درون یک اشاره گر دیگه ( FrmVar ) قرار دادیم
در OnClose هم فضای آدرس اشاره گر FrmVar که ما رو به frm می رسونه رو برابر nil قرار دادیم


فرض کنیم از متغیر استفاده کنیم و توی یه فرم بخایم ۵۰ تا فرم دیگرو باز کنیم... اسم متغییرا، تعداد و ...
برای نمایش یک فرم درگیر شدن توی مباحث اشاره گرها احتمال خطا رو بالا می بره
استفاده از مثال بالا برای نمایش چند باره فرم هم نمی تونه زیاد مفید باشه چون متغیر frm به صورت عمومی تعریف شده، یعنی برای هر نمونه از فرم باید یک متغیر درنظر گرفته بشه، درواقع همه نمونه های ایجاد شده، در پایان کارشون یک اشاره گر واحد رو می خوان برابر nil قرار بدن
اصولا وقتی شما میخواید چند نمونه از یک فرم رو بسازید دیگه نیاز به بررسی مقدار frm ندارید که نیاز باشه اون رو nil کنید و به نظرم انجام روشهایی مثل مثال بالا کار اضافه ای محسوب میشه
امکان nil کردن اشاره گر از داخل خود کلاس یک مزیت محسوب نمیشه و اگر این امکان وجود نداره هم یک ضعف نیست و به نظر خیلی منطقی هم نمیاد که وجود داشته باشه

به این نکته هم توجه کنید که برابر nil قرار دادن با Free کردن تفاوت داره، وقتی یک Object رو Create می کنید، در پایان کارش فضای درنظر گرفته شده برای اون Object باید Free بشه تا حافظه اضافی مصرف نشه، اما nil کردن "اشاره گر اون فضا" ( در اینجا frm ) اجباری نیست، با هر بار ساخت نمونه از کلاس، اشاره گر مقدار جدیدی می گیره و nil نکردن اون باعث ایجاد Memory Leak نمیشه

به هر حال، سوال پرسیده شده در مورد نمایش یک فرم در صورت عدم وجودش بود و برای رسیدن به این هدف هم به نظرم روشهای قبلی نسبت به مثال بالا ساده تر و منطقی تر هستند
مثلا چک کردن فرم های Screen :)