I'm wondering if this usage of events will cause memory leaks in Silverlight?
private void Button_Click(object sender, RoutedEventArgs e)
{
var test = new ChildWindow();
EventHandler closedEvent = null;
closedEvent =
(s, args) =>
{
test.Closed -= closedEvent;
if (test.DialogResult == true)
{
// something
}
};
test.Closed += closedEvent;
test.Show();
}
I have dozen sample usage of Closed event which are left wired and leaky. Just wondered if this sample would eliminate the memory leak?
Thanks.
I don`t see any reason for memory leak. On every button click u allocate new ChildWindow object, which will be garbaged after method ended, coz you not store reference.
Labda (its object too) will be garbaged after the ChildWindow object.
Related
Let's just say I need to get and set a View's height. In Android, it's known you can get a view height only after it's drawn. If you're using Java, many answers, one of the most well-known way is like this one below, taken from this answer:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
view.getHeight(); //height is ready
}
});
Thus I search C#/Xamarin version, and found this works:
int viewHeight = 0;
ViewTreeObserver vto = view.ViewTreeObserver;
vto.GlobalLayout += (sender, args) =>
{
viewHeight = view.Height;
};
Thing is, it fired again and again. In Java version, it can be removed with
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
How to do it in C#\Xamarin? Should I resort to using boolean properties to know whether it's executed or not? Is there not way to do it like the android one?
If you are using C# Events, avoid using anonymous events if you need to unsubscribe, or you can implement the IOnGlobalLayoutListener and add/remove the listener:
C# EventHandler Style:
Create an EventHandler method for the event to invoke:
void Globallayout_handler(object sender, EventArgs e)
{
// ViewTreeObserver.IOnGlobalLayoutListener events
}
Subscribe:
var viewTreeObserver = aView.ViewTreeObserver;
viewTreeObserver.GlobalLayout += Globallayout_handler;
Unsubscribe:
var viewTreeObserver = aView.ViewTreeObserver;
viewTreeObserver.GlobalLayout -= Globallayout_handler;
Java Listener Style in C#:
Add and implement ViewTreeObserver.IOnGlobalLayoutListener:
public class CustomButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer,
ViewTreeObserver.IOnGlobalLayoutListener
{
~~~~
public void OnGlobalLayout()
{
// ViewTreeObserver.IOnGlobalLayoutListener events
}
}
Now you can use the Java way to add and remove this listener:
aView.ViewTreeObserver.RemoveOnGlobalLayoutListener(this);
aView.ViewTreeObserver.AddOnGlobalLayoutListener(this);
Even though the answer given by ShshiHangover is correct in principle, the unsubscribing didn't work for me as expected (using the regular method #1).
The reason is probably that the ViewTreeObserver in the called method can be different from the one the event handler subscribed to, so removing it may not work (i.e., the handler method is called continuously).
The correct way of doing this is to unsubscribe from the event sender object while ensuring that IsAlive yields true:
void ViewTreeObserver_GlobalLayout(object sender, EventArgs e)
{
ViewTreeObserver vto = (ViewTreeObserver)sender;
if (vto.IsAlive) {
vto.GlobalLayout -= ViewTreeObserver_GlobalLayout;
}
}
Neither #Daniel or #SushiHangover methods would actually unsubscribe for me (maybe an sdk bug?). My only solution was to set a bool flag on first run. It would be nice to know how to actually unsubscribe however...
Getting the ViewTreeObserver via sender never seems to be IsAlive whereas getting the tree from the View does. However either way the event doesn't get properly removed.
private void Setup()
{
cameraView = FindViewById<SurfaceView>(Resource.Id.camera_view);
//need to wait for view to inflate to get size
isSetup = false;
ViewTreeObserver vto = cameraView.ViewTreeObserver;
vto.GlobalLayout += Vto_GlobalLayout;
}
void Vto_GlobalLayout(object sender, System.EventArgs e)
{
//this didn't work either
//ViewTreeObserver vto = cameraView.ViewTreeObserver;
//vto.GlobalLayout -= Vto_GlobalLayout;
ViewTreeObserver vto = (ViewTreeObserver)sender;
if (vto.IsAlive)
vto.GlobalLayout -= Vto_GlobalLayout; //even after removing it seems to continue to fire...
if (!isSetup)
{
isSetup = true;
DoYourCodeNow();
}
}
Can you tell me which is the best way to create an event or all the options are good?
// OPTION 1
buttonAlert.Click += delegate
{
textChange.Text = string.Format("Hello World");
};
// OPTION 2
buttonAlert.Click +=(sender, e) =>
{
textChange.Text = string.Format("Hello World");
};
// OPTION 3
buttonAlert.Click += delegate (object sender, EventArgs e)
{
textChange.Text = string.Format("Hello World");
};
Its just a matter of preference. In terms of performance are all equivalent.
So, choose based on what you need and prefer.
As a complement of my answer i like to alert that you must unsubscribe a event (-=) after subscrive (+=).
From the documentation:
To prevent your event handler from being invoked when the event is
raised, simply unsubscribe from the event. In order to prevent
resource leaks, it is important to unsubscribe from events before you
dispose of a subscriber object. Until you unsubscribe from an event,
the multicast delegate that underlies the event in the publishing
object has a reference to the delegate that encapsulates the
subscriber's event handler. As long as the publishing object holds
that reference, your subscriber object will not be garbage collected.
I would say that first option is the best when you don´t need the lambda parameters (sender, event).
Between second and third I would choose second because it´s cleaner (just a matter of preference.
You can also use a method as a delegate, and it´s probably the best if you want to manage memory correctly. When you use a delegate or lambda there is no way to unsubscribe. That means that even if you destroy or leave the activity/fragment, the object will remain in memory and the garbage collector won´t be able to clear it. In the case the user opens and closes this screen many times, you may get an OutOfMemoryException eventually. This happens very often in Android. This would be the solution:
protected override void OnResume()
{
base.OnResume();
buttonAlert.Click += OnButtonClick;
}
protected override void OnPause()
{
base.OnPause();
buttonAlert.Click -= OnButtonClick;
}
private void OnButtonClick(object sender, EventArgs e)
{
textChange.Text = string.Format("Hello World");
}
I am learning Xamarin Android, and I see a lot of official samples using lambdas to subscribe Click events. Something like: mButton.Click += (sender, args) => { ... } is very common. I think this pattern, using lambda, cannot unsubscribe the event.(Correct me if I am wrong :) )
Today I read this document: Cross-Platform Performance - Unsubscribe from Events. It says that we should unsubscribe events to prevent memory leaks.
Then I am confused. Should I unsubscribe all the Click events? I feel that since the mButton is a member of my Activity, when destroying my Activity, the mButton should also be destroyed and therefore it is not necessary to unsubscribe its Click event. Is is true? If so, then in what cases should I unsubscribe a event?
Thanks!
I would say it depends. As long there there are no references kept and the garbage collector can do his job, you don't have to. But otherwise it is good practice to do so to prevent memory leaks. So I prefer doing this.
To unsibscribe lambda events, just store it in a variable or field
EventHandler buttonOnClick = (sender, args) => button.Text = string.Format("{0} clicks!", count++);
button.Click += buttonOnClick;
button.Click -= buttonOnClick;
This is how I generally do it
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.LoginPage);
InflateViews ();
}
protected override void OnResume ()
{
base.OnResume ();
BindHandlers ();
}
protected override void OnPause ()
{
base.OnPause ();
UnBindHandlers ();
}
void InflateViews()
{
loginButton = FindViewById (Resource.Id.loginButton);
usernameField = FindViewById<EditText> (Resource.Id.userName);
passwordField = FindViewById<EditText> (Resource.Id.password);
forgotPassword = FindViewById (Resource.Id.forgotPassword);
}
void BindHandlers()
{
loginButton.Click+= LoginButton_Click;
forgotPassword.Click+= ForgotPassword_Click;
}
void ForgotPassword_Click (object sender, EventArgs e)
{
StartActivity (typeof(ForgotPasswordActivity));
}
void UnBindHandlers()
{
loginButton.Click-= LoginButton_Click;
forgotPassword.Click-= ForgotPassword_Click;
}
Absolutely yes!
To prevent memory leaks it is important to prevent cycling references and such. Take your time and search on SO and you will find a lot about this topic.
I have a timer running every 1/10 second (Interval = 100). It is in an infinte loop because I want to load a site on my WebBrowser control and get info into a span ID each time it loads. My problem is if this process runs for a long time, it'll be a memory leak.
In 30 minutes, memory usage can be 800MB or more. How do I prevent my program from continuously using more memory as it runs?
Here is the relevent code. It does not include any of the process creation code yet.
private void buttonBid_Click(object sender, EventArgs e)
{
ID = Convert.ToInt32(textBoxID.Text);
getItem();
return;
}
private void getItem()
{
webBrowser.Url = new Uri("http://www.test.com/?id=" + ID);
webBrowser.DocumentCompleted += webBrowser_DocumentCompleted;
return;
}
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
timerLoad.Start();
return;
}
private void timerLoad_Tick(object sender, EventArgs e)
{
var elem = webBrowser.Document.GetElementById("product_" + ID);
textBoxProduct.Text = Convert.ToString(elem.InnerText);
timerLoad.Dispose();
timerLoad.Start();
}
A possible source for memoryleaks is multiple event bindings.
You bind the DocumentCompleted event each time the GetItem method is invoked.
You should bind the DocumentCompleted once, for instance in the constructor of your class.
However if you pressed the button just once, this will not be your main problem.
I would like to recommend .NET Memory Profiler 4.0 to troubleshoot your problem.
Also, I have searched a little bit further and your problem might be in the use of the WebBrowser component: See this question.
This is something you should do only once at creation time or so
webBrowser.DocumentCompleted += webBrowser_DocumentCompleted;
These can be removed. At least I think they serve no purpose and might be problematic
timerLoad.Dispose();
timerLoad.Start();
If I read your code correct the timerLoad.Start(); should be a Stop(); as the Tick loads the document and should then stop working until the next buttonclick, right?
What is main reasons for window.ShowDialog() stackOverflowException in WPF? I receive this exception after 10-20 seconds when I call:
if(myWindow.ShowDialog() == true)
{
//other stuff
}
Window is shows up and works good, but then I receive this exception.
The generic cause of an SOE like this is having an event handler whose code causes the same event to be raised again. A simple example is:
private void textBox1_TextChanged(object sender, TextChangedEventArgs e) {
textBox1.Text += "a";
}
Type a letter, takes about 5 seconds for program to run out of stack space and bomb. Your primary weapon to diagnose exactly which event handler causes this problem is the debugger, look at the Call Stack window. You solve it by using a little helper variable that indicates that you expect the event to be fired again so you can ignore it. Like this:
bool changingText;
private void textBox1_TextChanged(object sender, TextChangedEventArgs e) {
if (changingText) return;
changingText = true;
try {
textBox1.Text += "a";
}
finally {
changingText = false;
}
}
The try/finally is not strictly necessary but wise if you expect to keep your program running after an exception.
Surprisingly a stack overflow exception can be caused by repeatedly calling window.ShowDialog asynchronously.
public MainWindow()
{
InitializeComponent();
TheCallDelegate = TheCall;
_timer = new DispatcherTimer();
_timer.Tick += _timer_Tick;
_timer.Start();
}
DispatcherTimer _timer = null;
void _timer_Tick(object sender, EventArgs e)
{
_timer.Dispatcher.BeginInvoke(TheCallDelegate);
}
Action TheCallDelegate;
void TheCall()
{
Window win = new Window();
win.ShowDialog();
}
As you can see there is no actual recursion here (or there shouldn't have been) but once the exception happens you can see that the call stack is indeed full.
Without looking at how Window.ShowDialog is implemented internally I can't say what is the deeper cause of this.