📜 ⬆️ ⬇️

Weak [weak] links in the new version of Delphi

Hello.

Embarcadero yesterday announced the release of a new version of Delphi RAD studio XE 10.1 ,
You can see the entire list of changes here , but I want to talk about the most valuable (for our company) improvement, namely the introduction of weak [weak] links to the classical compiler (Win32 / Win64).

Above the article gives details of the problem, so that those who want to see what they have done in Delphi, please under the cat.

So, we will consider as the example of using weak links, the interface container. Of course, weak links are necessary in other situations, but we will consider this particular case. In such a collection, we have the following problem: the elements of the container must have a link to the owner-collection (for example, to have a link to an element, it can be removed from the collection or simply accessed to it). In turn, the collection itself should also contain links to the elements. Since in previous versions of Delphi, we had the opportunity to declare only normal references (strong references), as a result, we received circular references in this collection, which made it impossible to correctly free memory. Usually, there were two ways out of this situation:
1. In the element class, a non-typed pointer for the FOwner field was used. Assigning an interface reference to such an index did not lead to an increase in the reference count, which solved the problem of a circular reference, but this code is not safe - if the collection dies before the element (a link to which has been assigned somewhere), there is no safe way to learn about deletion collection - owner. FOwner pointer - continued to point to memory already removed.
2. To solve the problem of the previous method, you can implement an alert for items about the collection being deleted. When deleting, the collection runs through all its elements and causes a certain method in them, which in turn “understates” the FOwner field. The disadvantage of this approach is the additional code and time required for its execution. In the case of our collection example, this is not so scary, but in other non-trivial examples this code can confuse logic and add errors.
And in the latest version of the XE10.1 Berlin, there is a humanly normal opportunity to solve this problem. All you need is to declare the FOwner field with the attribute [weak]. Such a link will still be strongly typed, but assigning it to which will not increase the reference count to the original object. In the case when the object (in our case, the owner-collection) is deleted from memory, such a link will automatically “become underestimated”, which will be an indicator that the object is deleted.
')
Let's write two interfaces, and 2 classes that implement these interfaces (this example is simplified to the limit, and the issues of speed optimization did not stand at all)
Description of interfaces and classes
type { } IXListItem = interface ['{02E680D6-9F86-4303-85B5-256ACD89AD46}'] function GetName: string; procedure SetName(const Name: string); property Name: string read GetName write SetName; end; { } IXList = interface ['{922BDB26-4728-46DA-8632-C4F331C5A013}'] function Add: IXListItem; function Count: Integer; function GetItem(Index: Integer): IXListItem; property Items[Index: Integer]: IXListItem read GetItem; procedure Clear; end; {  } TXListItem = class(TInterfacedObject, IXListItem) private [weak] FOwner: IXList; //     FName: string; public constructor Create(const Owner: IXList); destructor Destroy; override; procedure SetName(const Name: string); function GetName: string; end; {  } TXList = class(TInterfacedObject, IXList) private FItems: array of IXListItem; public function Add: IXListItem; function Count: Integer; function GetItem(Index: Integer): IXListItem; procedure Clear; destructor Destroy; override; end; 


As you can see in the TXListItem object there is a weak reference to the owner object.

The implementation of the TXListItem class, in the GetName method, we use our weak reference:
TXListItem
 {TXListItem} constructor TXListItem.Create(const Owner: IXList); begin FOwner := Owner; //    end; destructor TXListItem.Destroy; begin ShowMessage('Destroy ' + GetName); inherited; end; function TXListItem.GetName: string; var List: IXList; begin List := FOwner; if Assigned(List) then Exit(FName + '; owner assined!') else Exit(FName + '; owner NOT assined!'); end; procedure TXListItem.SetName(const Name: string); begin FName := Name; end; 


TXList class is trivial:
Txlist
 { TXList } function TXList.Add: IXListItem; var c: Integer; begin c := Length(FItems); SetLength(FItems, c + 1); Result := TXListItem.Create(Self); FItems[c] := Result; end; procedure TXList.Clear; var i: Integer; begin for i := 0 to Length(FItems) - 1 do FItems[i] := nil; end; function TXList.Count: Integer; begin Result := Length(FItems); end; destructor TXList.Destroy; begin Clear; ShowMessage('Destroy list'); inherited; end; function TXList.GetItem(Index: Integer): IXListItem; begin Result := FItems[Index]; end; 


We declare our variables:
var
 var Form1: TForm1; List: IXList; Item: IXListItem; 


And methods for creating and deleting objects.
methods
 procedure TForm1.btnListCreateAndFillClick(Sender: TObject); begin List := TXList.Create; //   List.Add.Name := 'item1'; //    List.Add.Name := 'item2'; Item := List.Add; //       Item.Name := 'item3'; end; procedure TForm1.btnListClearClick(Sender: TObject); begin List := nil; //   end; procedure TForm1.btnLastItemFreeClick(Sender: TObject); begin Item := nil; //   ,    end; procedure TForm1.FormCreate(Sender: TObject); begin ReportMemoryLeaksOnShutdown := true; //     end; 


An example of working with the announcement [weak] links:
image

Example of working without declaring [weak] links:


As you can see, without using [weak] links, the collection and the elements cyclically link to each other and they do not call destructors, and accordingly we receive a memory leak, which is what FastMM informs us about.

Also in the new version there is an attribute [unsafe] which is analogous to [weak], but when you delete an object to which it refers, the link is not automatically “underestimated”.
By the way, we already found one bug with it, which we sent to qc .

Thanks for the example and help in writing a colleague h_xandr .

upd. Link to the repository

Source: https://habr.com/ru/post/282035/


All Articles