I have the following code, it causes a memory leak.
The problem is the task, when I remove it, everything is fine and the View as well as the ViewModel are GCed. It seems like the Task is keeping a reference to UpdateTimeDate and hence the ViewModel. I tried various things, but none have worked, hoping someone has any idea or explanation why it is the case.
public class HeaderViewModel : Observable, IDisposableAsync
{
public HeaderViewModel (IDateTimeProvider dateTimeProvider)
{
TokenSource = new CancellationTokenSource();
ATask = Task.Run(
async () =>
{
while(!TokenSource.Token.IsCancellationRequested)
{
UpdateTimeData();
await Task.Delay(800);
}
IsDisposed = true;
},
TokenSource.Token);
UpdateTimeData();
void UpdateTimeData()
{
TimeText = dateTimeProvider.Now.ToString("HH:mm:ss");
DateText = dateTimeProvider.Now.ToString("dd.MM.yyyy");
}
}
public CancellationTokenSource TokenSource { get; set; }
public bool IsDisposed { get; set; }
public string TimeText
{
get => Get<string>();
set => Set(value);
}
public string DateText
{
get => Get<string>();
set => Set(value);
}
private Task ATask { get; set; }
public async Task Dispose()
{
TokenSource.Cancel();
while(!IsDisposed)
{
await Task.Delay(50);
}
TokenSource.Dispose();
ATask.Dispose();
ATask = null;
TokenSource = null;
}
}
This is the Timer based solution, it also causes a memory leak:
public class HeaderViewModel : Observable, IDisposableAsync
{
public HeaderViewModel(IDateTimeProvider dateTimeProvider)
{
DateTimeProvider = dateTimeProvider;
ATimer = new Timer(800)
{
Enabled = true
};
UpdateTimeData(this, null);
ATimer.Elapsed += UpdateTimeData;
}
public string TimeText
{
get => Get<string>();
set => Set(value);
}
public string DateText
{
get => Get<string>();
set => Set(value);
}
public bool IsDisposed { get; set; }
private IDateTimeProvider DateTimeProvider { get; }
private Timer ATimer { get; }
public async Task Dispose()
{
ATimer.Stop();
await Task.Delay(1000);
ATimer.Elapsed -= UpdateTimeData;
ATimer.Dispose();
IsDisposed = true;
}
private void UpdateTimeData(object sender, ElapsedEventArgs elapsedEventArgs)
{
TimeText = DateTimeProvider.Now.ToString("HH:mm:ss");
DateText = DateTimeProvider.Now.ToString("dd.MM.yyyy");
}
}