#c# #xamarin #xamarin.forms
#c# #xamarin #xamarin.forms
Вопрос:
Я создал простой проект для изучения проблемы с производительностью в моем Xamarin.Формирует приложение.
У меня есть два вложенных списка для классической структуры с основными деталями. В соответствии с шаблоном MVVM я реализовал интерфейс INotifyPropertyChanged для классов ViewModel и использовал ObservableCollection для списков. В главном представлении у меня есть два стандартных ListView, первый (masters) привязан к основной коллекции, второй (details) привязан к коллекции details выбранного элемента основного списка. Оба имеют CachingStrategy=»RecycleElement».
При запуске приложения UWP в инструментах диагностики Visual Studio можно увидеть использование памяти приложением. При выборе элемента списка мастеров отображается список сведений и увеличивается объем используемой памяти, при изменении выбранного элемента списка мастеров список сведений пополняется соответствующими элементами и используемая память увеличивается, также, если для отображения меньше элементов, если снова выбран предыдущий элемент мастеров, используемая память снова увеличивается. Каждый раз, когда в главном списке изменяются выбранные элементы и обновляется список сведений, используемая память увеличивается, никогда не уменьшаясь. Похоже, что объекты, используемые для рендеринга элементов списка сведений, никогда не собираются GC.
Я думаю, что это слишком очевидный эффект, чтобы быть ошибкой, которую никто никогда не замечал, но тестовый проект очень минимален, все viewmodels наследуются от класса MVVMLight ViewModelBase, все свойства используют метод Set, а тип всех свойств списка — ObservableCollection , единственный пользовательский код предназначен для начальной загрузки. Я думаю, что я совершаю ошибку, которую не вижу.
Ниже представлены модели просмотра и код просмотра
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
this.Load();
}
private RelayCommand _reloadCommand;
public RelayCommand ReloadCommand { get { return _reloadCommand ?? (_reloadCommand = new RelayCommand(this.Load)); } }
private ObservableCollection<ItemViewModel> _items;
public ObservableCollection<ItemViewModel> Items { get { return _items; } set { this.Set(ref _items, value); } }
private ItemViewModel _selectedItem;
public ItemViewModel SelectedItem { get { return _selectedItem; } set { this.Set(ref _selectedItem, value); } }
private SubitemViewModel _selectedSubitem;
public SubitemViewModel SelectedSubitem { get { return _selectedSubitem; } set { this.Set(ref _selectedSubitem, value); } }
private void Load()
{
int[] ranges = { 15, 10, 5, 20 };
this.Items = new ObservableCollection<ItemViewModel>(
from i in Enumerable.Range(1, 15)
select new ItemViewModel()
{
Label = $"Item {i}",
Items = new ObservableCollection<SubitemViewModel>(from s in Enumerable.Range(1, ranges[i % 4] )
select new SubitemViewModel() { Label = $"Subitem {i}.{s}" })
});
}
}
ItemViewModel.cs
public class ItemViewModel : ViewModelBase
{
private string _label;
public string Label { get { return _label; } set { this.Set(ref _label, value); } }
private ObservableCollection<SubitemViewModel> _items;
public ObservableCollection<SubitemViewModel> Items { get { return _items; } set { this.Set(ref _items, value); } }
}
SubItemViewModel.cs
public class SubitemViewModel : ViewModelBase
{
private string _label;
public string Label { get { return _label; } set { this.Set(ref _label, value); } }
}
MainView.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:App1.ViewModel"
x:Class="App1.MainPage">
<StackLayout>
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="Masters" />
<Label Grid.Row="0" Grid.Column="1" Text="Details" />
<ListView Grid.Column="0" Grid.Row="1"
ItemsSource="{Binding Items}"
CachingStrategy="RecycleElement"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:ItemViewModel">
<ViewCell>
<ViewCell.View>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Label}" />
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Grid.Column="1" Grid.Row="1"
ItemsSource="{Binding SelectedItem.Items}"
SelectedItem="{Binding SelectedSubitem, Mode=TwoWay}"
CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:SubitemViewModel">
<ViewCell>
<ViewCell.View>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Label}" />
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</StackLayout>
</ContentPage>
Полный тестовый проект можно скачать здесь:MemoryTest.zip
Комментарии:
1. Действительно ли это проблема? Можете ли вы показать некоторые реальные цифры? Если использование памяти увеличивается на несколько КБ за раз, GC просто не хочет включаться. В любом случае, это не утечка памяти, это управляемый код.
2. Запустите 51 МБ Элемент1-> 10 подпунктов 53 МБ Элемент2-> 5 подпунктов 53 МБ Элемент3-> 20 подпунктов 56 МБ Элемент4-> 15 подпунктов 58 МБ Элемент3->20 подпунктов 60 МБ Каждый элемент Элемент3 выбран, используемая память увеличивается на 2 МБ
3. Как @CodeCaster , сначала попробуйте принудительно выполнить сборку мусора и посмотрите, что произойдет. Если произошла утечка памяти, вы используете какой-то плагин MVVM, и может случиться так, что он протекает. Попробуйте использовать только встроенную в Xamarin MVVM.
4. Вероятно, утечка памяти — не самый лучший термин, но использование памяти растет, но никогда не уменьшается. Это означает, что некоторые объекты, которые, как я ожидаю, больше не будут использоваться, остаются используемыми и не могут быть освобождены GC. Я попытался заставить GC. При сборе в наборе SelectedItem инструменты производительности отображают событие GC, но использование памяти не изменяется. Я сомневаюсь, что в такой часто используемой библиотеке, как MVVMLight, есть такая ошибка, но я попытался напрямую реализовать INotifyPropertyChanged… То же поведение.
5. @MrWolf Привет, вот обсуждение управления памятью форм Xamarin: forums.xamarin.com/discussion/23097 /…