niedziela, 4 marca 2012

Memory leaks w Prism

Każda technologia ma swoje zalety i wady. Prism także nie jest pozbawiony wad. Jedną z bardziej widocznych wad jest wycieka pamięci podczas stosowania podregionów. Memory leak, czyli sytuacja w której obiekt jest przechowywany w pamięci, ale nie można się do niego odwołać jest z pewnością jedną z największych bolączek programistycznych. W tym poście postaram się wyjaśnić gdzie znajduje się owy wyciek oraz na podstawie wpisu na blogu Damian Schenkelman-a pokaże w jaki sposób można ominąć ten problem.
Cały problem można znaleźć oficjalnie opisany pod tym adresem.

Podany jest tam także przykład aplikacji, w którym widać efekt działania memory leak-a. Mamy następującą aplikację:


Całość jest zrozumiała i prosta. Po kliknięciu na przycisk Remove MainView from MainRegion oczekujemy, że główny widok wraz z jego pod widokami zostanie usunięty. Drugi przycisk Check RegionManager regions count, sprawdza ile jest aktualnie zarejestrowanych widoków.
Zobaczmy co się stanie w momencie kiedy najpierw usuniemy widoki a następnie wyświetlimy ilość zarejestrowanych widoków:


Jak widać zarejestrowanych widoków jest 3. Powinien być jeden. Pozostałe dwa widoki to TopRightView oraz BottomRightView. Nie zostały usunięte automatycznie.

Rozwiązaniem jest dosyć proste. W momencie zmiany w RegionManager należy przejść po wszystkich widokach regionu i usunąć je.
Realizuje je następująca klasa:


Code:
    public class ClearChildViewsRegionBehavior : RegionBehavior
    {
        public const string BehaviorKey = "ClearChildViews";


        protected override void OnAttach()
        {
            Region.PropertyChanged += RegionPropertyChanged;
        }

        void RegionPropertyChanged(object sender, System.ComponentModelPropertyChangedEventArgs e)
        {
            if (e.PropertyName != "RegionManager") return;
            if (Region.RegionManager != null) return;
            foreach (var dependencyObject in Region.Views.OfType<DependencyObject>())
            {
                dependencyObject.ClearValue(RegionManager.RegionManagerProperty);
            }
        }
    }

Następnie zostaje jeszcze podpięcie klasy w Bootstrapperze:


Code:
        protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
        {
            var regionBehaviorTypesDictionary = base.ConfigureDefaultRegionBehaviors();
            regionBehaviorTypesDictionary.AddIfMissing
                (ClearChildViewsRegionBehavior.BehaviorKey, typeof(ClearChildViewsRegionBehavior));
            return regionBehaviorTypesDictionary;
        }

Teraz po wykonaniu wcześniej opisanego usunięcia i zliczeniu ilości widoków w regionie powinniśmy uzyskać następujący wynik:

Brak komentarzy:

Prześlij komentarz