From e36017b5fe3316b5f9d2d869574d7806f4c1c0d1 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Mon, 23 Jul 2018 22:22:07 +0200 Subject: [PATCH] perf(tree): leaking reference through mostRecentTreeNode Along the same lines as #12269. Clears out the `mostRecentTreeNode` once the last tree node is destroyed, in order to avoid a memory leak. --- src/cdk/tree/tree.spec.ts | 16 +++++++++++++++- src/cdk/tree/tree.ts | 8 +++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/cdk/tree/tree.spec.ts b/src/cdk/tree/tree.spec.ts index e7ffa605c7aa..a91038c61aa1 100644 --- a/src/cdk/tree/tree.spec.ts +++ b/src/cdk/tree/tree.spec.ts @@ -17,7 +17,7 @@ import {TreeControl} from './control/tree-control'; import {FlatTreeControl} from './control/flat-tree-control'; import {NestedTreeControl} from './control/nested-tree-control'; import {CdkTreeModule} from './index'; -import {CdkTree} from './tree'; +import {CdkTree, CdkTreeNode} from './tree'; import {getTreeControlFunctionsMissingError} from './tree-errors'; @@ -35,6 +35,20 @@ describe('CdkTree', () => { }).compileComponents(); } + it('should clear out the `mostRecentTreeNode` on destroy', () => { + configureCdkTreeTestingModule([SimpleCdkTreeApp]); + const fixture = TestBed.createComponent(SimpleCdkTreeApp); + fixture.detectChanges(); + + // Cast the assertions to a boolean to avoid Jasmine going into an + // infinite loop when stringifying the object, if the test starts failing. + expect(!!CdkTreeNode.mostRecentTreeNode).toBe(true); + + fixture.destroy(); + + expect(!!CdkTreeNode.mostRecentTreeNode).toBe(false); + }); + describe('flat tree', () => { describe('should initialize', () => { let fixture: ComponentFixture; diff --git a/src/cdk/tree/tree.ts b/src/cdk/tree/tree.ts index 11e4052ed8ac..e0ead62e1ca2 100644 --- a/src/cdk/tree/tree.ts +++ b/src/cdk/tree/tree.ts @@ -290,7 +290,7 @@ export class CdkTree 'class': 'cdk-tree-node', }, }) -export class CdkTreeNode implements FocusableOption, OnDestroy { +export class CdkTreeNode implements FocusableOption, OnDestroy { /** * The most recently created `CdkTreeNode`. We save it in static variable so we can retrieve it * in `CdkTree` and set the data to it. @@ -328,6 +328,12 @@ export class CdkTreeNode implements FocusableOption, OnDestroy { } ngOnDestroy() { + // If this is the last tree node being destroyed, + // clear out the reference to avoid leaking memory. + if (CdkTreeNode.mostRecentTreeNode === this) { + CdkTreeNode.mostRecentTreeNode = null; + } + this._destroyed.next(); this._destroyed.complete(); }