Optimalizace výkonu: Chování objektu

Pochopení vnitřního chování objektů WPF vám pomůže zajistit správné kompromisy mezi funkcemi a výkonem.

Odebrání obslužných rutin událostí u objektů může udržovat objekty naživu

Delegát, který objekt předává do své události, je účinně odkaz na tento objekt. Obslužné rutiny událostí proto můžou udržovat objekty naživu déle, než se čekalo. Při čištění objektu, který je zaregistrovaný k naslouchání události objektu, je nezbytné odebrat tento delegát před uvolněním objektu. Udržování nepotřebných objektů naživu zvyšuje využití paměti aplikace. To platí zejména v případě, že objekt je kořenem logického stromu nebo vizuálního stromu.

WPF zavádí slabý model naslouchacího procesu událostí pro události, které můžou být užitečné v situacích, kdy je obtížné sledovat vztahy životnosti objektu mezi zdrojem a naslouchacím procesem. Tento vzor používají některé existující události WPF. Pokud implementujete objekty s vlastními událostmi, může být tento vzor použit pro vás. Podrobnosti najdete v tématu Slabé vzory událostí.

Existuje několik nástrojů, jako je profiler CLR a prohlížeč pracovních skupin, které mohou obsahovat informace o využití paměti zadaného procesu. Profiler CLR obsahuje řadu velmi užitečných zobrazení profilu přidělení, včetně histogramu přidělených typů, grafů přidělení a volání, časového řádku zobrazující uvolňování paměti různých generací a výsledný stav spravované haldy za těmito kolekcemi a strom volání zobrazující přidělení jednotlivých metod a načtení sestavení. Další informace najdete v tématu Výkon.

Vlastnosti a objekty závislostí

Obecně platí, že přístup k vlastnosti závislosti objektu DependencyObject není pomalejší než přístup k vlastnosti CLR. I když je pro nastavení hodnoty vlastnosti menší režie výkonu, získání hodnoty je stejně rychlé jako získání hodnoty z vlastnosti CLR. Snížením nízké režie na výkon je skutečnost, že vlastnosti závislostí podporují robustní funkce, jako jsou datové vazby, animace, dědičnost a styly. Další informace naleznete v tématu Přehled vlastností závislostí.

Optimalizace DependencyProperty

Vlastnosti závislostí byste ve své aplikaci měli definovat velmi pečlivě. Pokud se váš DependencyProperty vliv týká pouze možností metadat typu vykreslení, a ne jiných možností metadat, jako AffectsMeasurejsou například , byste je měli označit jako takové přepsáním jeho metadat. Další informace o přepsání nebo získání metadat vlastností naleznete v tématu Metadata vlastností závislostí.

Může být efektivnější mít obslužnou rutinu změny vlastnosti zneplatnit míru, uspořádat a vykreslit ručně, pokud ne všechny změny vlastností skutečně ovlivňují míru, uspořádání a vykreslení. Můžete se například rozhodnout znovu vykreslit pozadí jenom v případě, že je hodnota větší než nastavený limit. V tomto případě by obslužná rutina změny vlastnosti zneplatnila vykreslení pouze v případě, že hodnota překročí nastavený limit.

Vytvoření zděděděné závislosti Není volné

Ve výchozím nastavení jsou registrované vlastnosti závislostí neděditelné. Můžete však explicitně vytvořit libovolnou zděděnou vlastnost. I když je to užitečná funkce, převod vlastnosti, která má být zděděná, má vliv na výkon zvýšením doby pro zneplatnění vlastnosti.

Pečlivě používejte popisovač RegisterClassHandler.

Při volání RegisterClassHandler můžete uložit stav instance, je důležité vědět, že obslužná rutina je volána u každé instance, což může způsobit problémy s výkonem. Použijte RegisterClassHandler pouze v případě, že vaše aplikace vyžaduje, abyste uložili stav instance.

Nastavení výchozí hodnoty pro DependencyProperty během registrace

Při vytváření DependencyProperty , který vyžaduje výchozí hodnotu, nastavte hodnotu pomocí výchozích metadat předaných jako parametr Register metody DependencyPropertymetody . Tuto techniku použijte místo nastavení hodnoty vlastnosti v konstruktoru nebo u každé instance prvku.

Nastavení hodnoty PropertyMetadata pomocí registru

Při vytváření DependencyPropertymáte možnost nastavit PropertyMetadata pomocí metod Register nebo OverrideMetadata metod. I když objekt může mít statický konstruktor, který se má volat OverrideMetadata, nejedná se o optimální řešení a bude mít vliv na výkon. Nejlepšího výkonu PropertyMetadata dosáhnete nastavením během volání na Registerhodnotu .

Ukotvené objekty

A Freezable je speciální typ objektu, který má dva stavy: unfrozen a frozen. Zmrazení objektů, kdykoli je to možné, zlepší výkon aplikace a sníží jeho pracovní sadu. Další informace naleznete v tématu Zamrznutelné objekty Přehled.

Každá z nich FreezableChanged událost, která se vyvolá pokaždé, když se změní. Oznámení o změnách jsou ale nákladná z hlediska výkonu aplikace.

Podívejte se na následující příklad, ve kterém každý Rectangle používá stejný Brush objekt:

rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
rectangle_1.Fill = myBrush
rectangle_2.Fill = myBrush
rectangle_3.Fill = myBrush
' ...
rectangle_10.Fill = myBrush

WPF ve výchozím nastavení poskytuje obslužnou rutinu události pro SolidColorBrush událost objektu Changed , aby bylo možné zneplatnit Rectangle vlastnost objektu Fill . V takovém případě je nutné při každém vyvolání Changed funkce zpětného volání vyvolat při každém SolidColorBrush vyvolání funkce Rectanglezpětného volání akumulace těchto vyvolání funkce zpětného volání významné snížení výkonu. Kromě toho je velmi náročné na výkon přidávat a odebírat obslužné rutiny v tomto okamžiku, protože aplikace by musela procházet celý seznam. Pokud váš scénář aplikace nikdy nezmění SolidColorBrush, budete platit náklady na údržbu Changed obslužných rutin událostí zbytečně.

Zmrazení Freezable může zlepšit svůj výkon, protože už nemusí navýšit prostředky na udržování oznámení o změnách. Následující tabulka ukazuje velikost jednoduché SolidColorBrush , pokud je jeho IsFrozen vlastnost nastavena na true, ve srovnání s tím, kdy není. To předpokládá použití jednoho štětce na Fill vlastnost deseti Rectangle objektů.

Kraj Velikost
Zmrazené SolidColorBrush 212 Bajty
Nezablokované SolidColorBrush 972 Bajty

Následující ukázka kódu ukazuje tento koncept:

Brush frozenBrush = new SolidColorBrush(Colors.Blue);
frozenBrush.Freeze();
Brush nonFrozenBrush = new SolidColorBrush(Colors.Blue);

for (int i = 0; i < 10; i++)
{
    // Create a Rectangle using a non-frozed Brush.
    Rectangle rectangleNonFrozen = new Rectangle();
    rectangleNonFrozen.Fill = nonFrozenBrush;

    // Create a Rectangle using a frozed Brush.
    Rectangle rectangleFrozen = new Rectangle();
    rectangleFrozen.Fill = frozenBrush;
}
Dim frozenBrush As Brush = New SolidColorBrush(Colors.Blue)
frozenBrush.Freeze()
Dim nonFrozenBrush As Brush = New SolidColorBrush(Colors.Blue)

For i As Integer = 0 To 9
    ' Create a Rectangle using a non-frozed Brush.
    Dim rectangleNonFrozen As New Rectangle()
    rectangleNonFrozen.Fill = nonFrozenBrush

    ' Create a Rectangle using a frozed Brush.
    Dim rectangleFrozen As New Rectangle()
    rectangleFrozen.Fill = frozenBrush
Next i

Změněné obslužné rutiny u nezamrznutelných zamrznutí můžou udržovat objekty naživu.

Delegát, který objekt předá Freezable události objektu Changed , je efektivně odkaz na tento objekt. Changed Obslužné rutiny událostí proto můžou udržovat objekty naživu déle, než se čekalo. Při čištění objektu, který je zaregistrovaný k naslouchání Freezable události objektu Changed , je důležité odebrat tento delegát před uvolněním objektu.

WPF také interně připojí Changed události. Například všechny vlastnosti závislosti, které berou Freezable jako hodnotu, budou automaticky naslouchat událostem Changed . Vlastnost Fill , která přebírá Brush, ilustruje tento koncept.

Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
Dim myBrush As Brush = New SolidColorBrush(Colors.Red)
Dim myRectangle As New Rectangle()
myRectangle.Fill = myBrush

Při přiřazení delegáta myRectangle.Fillodkazujícího zpět na Rectangle objekt se přidá do události objektu SolidColorBrushChanged.myBrush To znamená, že následující kód ve skutečnosti myRect nemá nárok na uvolňování paměti:

myRectangle = null;
myRectangle = Nothing

V tomto případě myBrush je stále naživu myRectangle a zavolá se k němu, když aktivuje událost Changed . Všimněte si, že přiřazení myBrush k vlastnosti nové Rectangle bude jednoduše přidat další obslužnou rutinu události do myBrushFill .

Doporučeným způsobem, jak tyto typy objektů vyčistit, je odebrat Brush z Fill vlastnosti, která zase odebere obslužnou rutinu Changed události.

myRectangle.Fill = null;
myRectangle = null;
myRectangle.Fill = Nothing
myRectangle = Nothing

Virtualizace uživatelského rozhraní

WPF také poskytuje variantu StackPanel prvku, který automaticky virtualizuje podřízený obsah vázaný na data. V tomto kontextu slovo virtualizuje techniku, pomocí které se podmnožina objektů generuje z většího počtu datových položek na základě toho, které položky jsou viditelné na obrazovce. Je náročné, jak z hlediska paměti, tak procesoru, vygenerovat velký počet prvků uživatelského rozhraní, když v daném okamžiku může být na obrazovce jen několik. VirtualizingStackPanel (prostřednictvím funkcí poskytovaných VirtualizingPanel) vypočítá viditelné položky a pracuje s ItemContainerGenerator objektem ItemsControl (například ListBox ) ListViewa vytváří pouze prvky pro viditelné položky.

Při optimalizaci výkonu se vizuální objekty pro tyto položky generují nebo udržují aktivní pouze v případě, že jsou viditelné na obrazovce. Pokud už nejsou v zobrazitelné oblasti ovládacího prvku, mohou být objekty vizuálu odebrány. To není zaměňováno s virtualizací dat, kdy datové objekty nejsou všechny v místní kolekci, spíše streamovány podle potřeby.

Následující tabulka ukazuje uplynulý čas přidání a vykreslení 5000 TextBlock prvků do StackPanel a a VirtualizingStackPanel. V tomto scénáři měření představují čas mezi připojením textového řetězce k ItemsSource vlastnosti ItemsControl objektu k času, kdy prvky panelu zobrazují textový řetězec.

Panel hostitelů Doba vykreslování (ms)
StackPanel 3210
VirtualizingStackPanel 46

Viz také