Защо присвояването на обект се губи, когато използвам free()?

Аз имам проблем. Например, когато се опитам да получа обект от функция или присвояване.

//Global.-
MyObject:= TMyObject.Create();  
//Local assign.-
myObj:= MyObject;

В горния случай, ако опитам това:

//Global variable.-
MyObject:= TMyObject.Create();
MyObject.MyData:= 'Salam world';  
//Local variable.-
myObj:= MyObject;
MyObject.Free();
...
ShowMessage(myObj.MyData); //Don't show the data.-

Показване на нарушение на достъпа за това:

//Global variable.-
MyObject:= TMyObject.Create();
MyObject.MyData:= 'Salam world';
...
//Local variable.-
myObj:= TMyObject.Create();
myObj:= MyObject;
..
MyObject.Free();
...
ShowMessage(myObj.MyData);

Това, от което се нуждая, е да освободя глобалната променлива преди локалната променлива. Но мога да запазя копието на данните в разпределението.

Благодаря ти за помощта!


person JMCrash    schedule 02.01.2016    source източник
comment
когато направите това myObj:= MyObject, и двете променливи стават един и същ обект. Така че, когато извиквате MyObject.Free() след това - променливата myObj също се освобождава. И така, не мога да разбера защо се опитвате да освободите MyObject обект, ако искате да използвате данните му по-нататък в кода?   -  person ankhzet    schedule 02.01.2016
comment
Защо ви трябват две променливи?   -  person David Heffernan    schedule 02.01.2016


Отговори (2)


Не можете да освободите променлива, можете само да освободите/унищожите обект.

Така че, когато във вашия код се обадите

MyObject.Free();

Вие всъщност унищожавате обекта, към който MyObject променливата препраща/пинтва. И това е същият обект, към който сочи/посочва вашата променлива myObj.

И причината, поради която и двамата сочат към един и същи обект, е, че когато сте се обадили

myObj:= MyObject;

просто сте копирали данните, използвани от вашата програма за препратка към този обект от променливата mObj в променливата MyObject.

Виждате, че в Delphi/Object Pascal има два типа променливи. Реферирани и нереферирани променливи.

Нереферентните променливи са прости променливи като Integers например. Те директно сочат към мястото в паметта, съдържащо техните данни. И техният размер е точно същият като размера на блока памет, към който сочат.

Референтните променливи, които са всички променливи, рефериращи класове, интерфейси или низове, са различни. Вместо да сочат директно към блока памет, съдържащ обекта, те вместо това сочат към специална препратка към този обект (инстанция). След това тази препратка всъщност съдържа информация къде в паметта се намира този обект и колко голям е той.

Основното предимство на реферираните променливи е, че можете да имате множество реферирани променливи, всички рефериращи към един и същ обект. Това е особено полезно, когато имате различни имена на тези променливи в различни части на кода.

Но основният им недостатък е, че трябва да имате подходящ механизъм, за да сте сигурни, че паметта, която съдържа обекта, към който се отнасят, се освобождава в точното време. Ето защо трябва да извикате MyObject.Free или MyObject.Destroy, за да се освободи тази памет.

Сега, в случай на използване на разширено препращане, не е необходимо сами да извиквате деструктора на обекти, тъй като ARC съдържа свой собствен механизъм, за да разбере кога даден обект вече не е необходим и може да бъде освободен (броят на препратките достига нула).

Във вашия въпрос вие заявявате, че искате вашата глобална променлива да не сочи повече към вашия обект, но все пак вашата локална променлива да сочи към този обект.

Предполагам, че има две възможни причини за това:

Първо може да искате вашият глобален обект да препраща към друг обект, като същевременно запазва препратка към стария обект в локалния обхват. За да направите това, можете просто да присвоите нова препратка към обект на вашата глобална обектна променлива

MyGlobalObject := SomeNewObject;

or

MyGlobalObject := TSomeObject.Create;

на второ място може да искате вашата глобална обектна променлива вече да не препраща към валиден обект. В този случай трябва само да го зададете на специално състояние nil

MyGlobalObject := nil;

Във всеки случай и в двата случая се уверете, че преди да промените променливата на глобалния си обект, имате поне една друга променлива, препращаща към този обект. Защо?

Защото, ако промените последната си променлива, сочеща към някакъв обект, за да сочи към друг обект или изобщо да не сочи, вие губите способността да взаимодействате и да освободите паметта на първоначалните обекти. В програмната терминология такива сценарии се наричат ​​течове на памет.

person SilverWarior    schedule 02.01.2016
comment
в Delphi/Object Pascal има два типа променливи. Реферирани и нереферирани променливи. Всъщност трябва да пише: в Delphi/Object Pascal има референтни типове и стойностни типове - person Stefan Glienke; 04.01.2016
comment
@StefanGlienke Стриктно спазване на официалната терминология, да. Но стриктното заместване на това, което казах с това, което предлагате, вероятно би объркало OP още повече. Особено ако той не разбира основната дефиниция на променливата и как типът на променливата влияе върху начина, по който данните се присвояват към нея. Затова реших да пропусна използването на официална терминология и вместо това да използвам описателна терминология, за да помогна на OP да разбере какво се опитвам да му кажа. И въз основа на факта, че OP маркира моя отговор като правилен, дори и преди това той маркира този на Ken White като правилен, предполагам, че съм успял. - person SilverWarior; 04.01.2016

Когато изпълнявате присвояването по начина, по който сте, вие не създавате копие на обекта; вие просто правите две променливи, които препращат към един и същ обект. Освобождаването на този обект означава, че втората променлива сочи към обекта, който сте освободили.

//Global variable.-
MyObject:= TMyObject.Create();
MyObject.MyData:= 'Salam world';  

Следващият ред не прави второ копие на обекта. Това просто кара myObj също да сочи към MyObject.

//Local variable.-
myObj:= MyObject;

Можете сами да видите това, като използвате следния код:

MyObject := TMyObject.Create;
MyObject.MyData := 'Some text';
ShowMessage(MyObject.MyData);      // Shows 'Some text'

myObj := MyObject;
// Change local variable property value
myObj.MyData := 'Different text';
// Display global variable property value
ShowMessage(MyObject.MyData);     // Shows 'Different text'

Този ред освобождава MyObject, което означава, че myObj сега препраща към обекта, който току-що сте освободили, тъй като той просто сочи към съдържанието на MyObject.

MyObject.Free();

Ако искате да направите копие, използвайте Assign вместо това.

myObj := TMyObject.Create;
myObj.Assign(MyObject);
person Ken White    schedule 02.01.2016
comment
Струва си да се отбележи, че във вашите собствени класове трябва сами да имплементирате метода Assign, имплементацията по подразбиране в TPersistent извиква AssignTo на източника, който вероятно също не е имплементиран, така че в крайна сметка получавате изключение. - person Yuriy Afanasenkov; 02.01.2016