Interfaces in Delphi
spacer
Autor Nachricht
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 15840
Erhaltene Danke: 741

XP, W7 x64 (Chrome, IE9, FF), Debian, (OSX 10.7)
RAD XE 2, Java (NB), C++, C# (VS 2010), JS/HTML, PHP, Lazarus
BeitragVerfasst: Di 24.01.12 19:52 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Damit gearbeitet hab ich noch nicht, aber die Samples sehen sehr elegant aus - anders als man das von Delphi sonst kennt.
So auf den ersten Blick sieht es ein wenig ähnlich wie das verlinkte Spring Framework aus, gerade im Hinblick auf das Registrieren von Interfaces. Aber ansonsten habe ich in den paar Beispielen, die ich jetzt angeschaut habe, nichts wirklich besonderes gesehen. So ähnlich sieht es auch mit Klassenmethoden und anonymen Methoden aus wie sie in den aktuellen Delphiversionen auch intern oft verwendet werden.

Hast du da vielleicht gerade ein Beispiel, das es sich lohnt näher anzuschauen? Das sind so viele... ;-)
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
Werbung ausblenden? Dann registriere Dich kostenlos. Weitere Gründe für eine Registrierung.


Werbung ausblenden? Dann registriere Dich kostenlos. Weitere Gründe für eine Registrierung.
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic star
Beiträge: 2682
Erhaltene Danke: 26



BeitragVerfasst: Di 24.01.12 23:43 
user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
Was Nick Hodges da schreibt klingt nach: Nutze Interfaces und du brauchst dich nie wieder um die Laufzeit von Objekten kümmern.

Das ist eine Illusion. Du kannst leicht zirkuläre Abhängigkeiten kriegen und so verhindern, dass Objekte jemals freigegeben werden und es kann passieren, dass Objekte zu früh freigegeben werden (z.B. bei Events, d.h. "Delegates"). Um das zu verhindern musst du in Delphi teilweise recht üble Hacks machen.

Einfaches Beispiel für ein Memory-Leak:
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
type
ITest = interface
end;

TTest = class(TInterfacedObject, ITest)
private
FTest: ITest;
public
constructor Create;
end;

constructor TTest.Create;
begin
FTest := Self;
end;


Ausserdem ist die Referenzzählung i.d.R. weniger effizient als ein GC, da bei jeder Zuweisung ein InterlockedIncrement gemacht werden muss.

Ein GC ist viel mächtiger. Ein GC kann in einem Wisch viele Objekte freigeben. Ein GC kann den Speicher defragmentieren. Ein GC kann verwaiste Gruppen erkennen, die sich gegenseitig zirkulär referenzieren und die ganze Gruppe freigeben. In C# hast du zudem schwache Referenzen und es ist nicht möglich, dass ein Objekt zu früh freigegeben wird.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 452
Erhaltene Danke: 10

Win XP, Debian GNU/Linux
Delphi 2007 Enterprise, Java, C#
BeitragVerfasst: Mi 25.01.12 12:44 
user profile icondelfiphan hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
Was Nick Hodges da schreibt klingt nach: Nutze Interfaces und du brauchst dich nie wieder um die Laufzeit von Objekten kümmern.

Das ist eine Illusion.

Das habe ich vermutet oder zumindest befürchtet.

In TInterfacedObject wird ja kein Free ausgeführt, sondern ein Destroy. Das heißt dann ja auch, dass ich Instanzen auf Interfaces oder zumindest wenn ich von TInterfacedObject erbe, nicht selbst freigeben darf, richtig?

Ich kann mich dann also gar nicht mehr selbst darum kümmern. Es wird somit nur ein Problem (ich muss mich selbst drum kümmern) durch ein anderes (die Objekte kümmern sich selber - schlecht) ersetzt, oder?
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 15840
Erhaltene Danke: 741

XP, W7 x64 (Chrome, IE9, FF), Debian, (OSX 10.7)
RAD XE 2, Java (NB), C++, C# (VS 2010), JS/HTML, PHP, Lazarus
BeitragVerfasst: Mi 25.01.12 13:27 
user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
Ich kann mich dann also gar nicht mehr selbst darum kümmern.
Richtig. Solange die Referenzzählung aktiv ist, darfst du nichts mehr selbst freigeben.

Und das ist genau was ich aber manchmal möchte und was mich an C# und Java immer wieder stört wie gesagt. So schön die automatische Freigabe auch ist, wenn man das nicht auch selbst steuern kann (GC bei Java und C#) oder nur durch dreckige Tricks (Interfaces in Delphi), gibt es immer wieder Situationen wo man sich so richtig schön verrenken muss, damit die Automatismen arbeiten wie man es möchte.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Moderator
Beiträge: 2837
Erhaltene Danke: 182

Win 2000, Win XP
Delphi 7, Turbo Delphi Exp.
BeitragVerfasst: Mi 25.01.12 13:53 
Ich muss mich grade ernsthaft verrenken, um eine Situation zu finden in der ich was selber freigeben will.
Wenn ich Speicher freigeben will, dann brauch ich den doch nicht mehr? Dann kann ich einfach die Referenz wegwerfen und der GC kümmert sich drum. Wenn ich die noch brauche, brauch ich die Daten dahinter aber auch noch... was programmierst du komisches? :P

Dass das in Delphi etwas kaputt ist stimmt aber. Wie eigentlich alles außer dem GUI-Designer. Aber ich schweife ab :P

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Ich code EdgeMonkey -~==~- #ee-lounge in Freenode
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 452
Erhaltene Danke: 10

Win XP, Debian GNU/Linux
Delphi 2007 Enterprise, Java, C#
BeitragVerfasst: Mi 25.01.12 14:07 
Eigentlich mag ich das Konzept der GC ganz gerne und finde es teilweise nervig, dass ich mich in Delphi selbst drum kümmern muss.

Finde dann immer wieder Stolpersteine wie eine TStringList oder TObjectList die auf OwnsObjects=True gesetzt ist und ich somit versuche die Objekte doppelt freizugeben - was dann immer erst zu ewigen Suchen führt.
Dann sind da immer wieder stellen an denen man das try..finally doch vergessen hat und schon hat man ein Memory-Leak. Das kann passieren wenn man eben aus der Java- oder .NET-Welt kommt und die GC gewohnt ist.

Aber die Interface-Lösung von Delphi mit der internen Referenzzählung klingt dann ja auch nur erstmal nach 'ner guten Idee. Was mich auch stört ist, dass ich dann extra eine TInterfaceList brauche, mein Interface nicht auf eine Klasse casten kann und auch kein if (Intf is TObject) then machen kann, da das Interface ja kein TObject ist, auch wenn ich in dem konkreten Fall ja eine Instanz einer Klasse habe und wissen will, welche Klasse ich hier genau habe.
Kann man jetzt argumentieren, dass es sich dann um ein Designproblem meinerseits handelt, trotzdem finde ich das durchaus nervig...

@user profile iconjaenicke: Du kannst die GC in Java doch einfach manuell aufrufen - auch wenn man das nicht sollte.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 15840
Erhaltene Danke: 741

XP, W7 x64 (Chrome, IE9, FF), Debian, (OSX 10.7)
RAD XE 2, Java (NB), C++, C# (VS 2010), JS/HTML, PHP, Lazarus
BeitragVerfasst: Mi 25.01.12 14:46 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Dann kann ich einfach die Referenz wegwerfen und der GC kümmert sich drum.
Wenn es denn so wäre...
Ich hatte in einem Programm vor etwa einem halben Jahr in C# ca. 1,5 GiB RAM belegt, dann die Referenz auf diese Daten überschrieben und weitergemacht (in einer Schleife). Leider hat der GC aber nicht aufgeräumt, so dass nach wenigen Sekunden die Meldung kam, dass der Speicher voll sei.

Nach diversen Versuchen wie Auslagerung in eigene Methoden hatte ich es so gelöst, dass sich die Exe für jeden Durchlauf erneut selbst aufgerufen hat...

Dann habe ich es in Delphi gemacht und es lief sofort ohne Probleme und brauchte auch nur 1 GiB RAM maximal.

// EDIT:
user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
mein Interface nicht auf eine Klasse casten kann und auch kein if (Intf is TObject) then machen kann, da das Interface ja kein TObject ist, auch wenn ich in dem konkreten Fall ja eine Instanz einer Klasse habe und wissen will, welche Klasse ich hier genau habe.
Doch, das geht. Ich weiß nicht ab welcher Delphiversion, aber mit den aktuellen Versionen (XE, XE2) auf jeden Fall. Ich schätze ab Delphi 2010, evtl. ab 2009.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic star
Beiträge: 2682
Erhaltene Danke: 26



BeitragVerfasst: Mi 25.01.12 19:13 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Dann kann ich einfach die Referenz wegwerfen und der GC kümmert sich drum.
Wenn es denn so wäre...
Ich hatte in einem Programm vor etwa einem halben Jahr in C# ca. 1,5 GiB RAM belegt, dann die Referenz auf diese Daten überschrieben und weitergemacht (in einer Schleife). Leider hat der GC aber nicht aufgeräumt, so dass nach wenigen Sekunden die Meldung kam, dass der Speicher voll sei.

Das ist dann ziemlich sicher ein Bug in deinem C# Programm. Der GC in .NET ist nämlich ganz schön fleissig.

Du musst vor allem bei Events aufpassen. Denn Delegates, die man bei Events braucht, enthalten neben dem Methodenpointer auch eine starke Referenz auf das Objekt. In Delphi würdest du bei einem solchen Bug zwar den Speicher rechtzeitig freibekommen, würdest aber irgendwann mal eine AccessViolation kriegen. In .NET bleibt das Objekt halt am leben. Gibt aber gute Tools, die den ganzen Speicherbaum schön graphisch anzeigen können.

Wenn's was explizig zum Aufräumen gibt, verwende IDisposable. Dann hilft dir der Compiler bzw. FxCop auch immer schön, wenn du vergisst, aufzuräumen.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
baka0815
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 452
Erhaltene Danke: 10

Win XP, Debian GNU/Linux
Delphi 2007 Enterprise, Java, C#
BeitragVerfasst: Mo 30.01.12 17:04 
Sollte man in Delphi dann statt Interfaces abstrakte Klassen verwenden?
Dann muss ich mich zwar selber um den Speicher kümmern, habe aber die damit verbundenen Nachteile nicht.

Oder haben Abstrakte Klassen andere Nachteile - außer dass man diese instantiieren kann?
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic star
Beiträge: 2682
Erhaltene Danke: 26



BeitragVerfasst: Mo 30.01.12 18:54 
Eine abstrakte Basisklasse geht natürlich ein bisschen in die gleiche Richtung. Aber es gibt's keine Mehrfachvererbung, daher muss zuoberst in der Hierarchie immer diese abstrakte Klasse sein. Ein Interface kann man einfach an eine bestehende Klasse anhängen.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
home home