Skip to content

Commit

Permalink
feat: Trim MMKV on memoryWarning - GC safe via WeakRef!
Browse files Browse the repository at this point in the history
  • Loading branch information
mrousavy committed Sep 10, 2024
1 parent 437fd33 commit eed7da4
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 5 deletions.
7 changes: 2 additions & 5 deletions package/src/MMKV.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { AppState } from 'react-native';
import { createMMKV } from './createMMKV';
import { createMockMMKV } from './createMMKV.mock';
import { isTest } from './PlatformChecker';
import type { Configuration } from './NativeMmkv';
import type { Listener, MMKVInterface, NativeMMKV } from './Types';
import { addMemoryWarningListener } from './MemoryWarningListener';

const onValueChangedListeners = new Map<string, ((key: string) => void)[]>();

Expand All @@ -26,10 +26,7 @@ export class MMKV implements MMKVInterface {
: createMMKV(configuration);
this.functionCache = {};

AppState.addEventListener('memoryWarning', () => {
// when we have a memory warning, delete unused keys by trimming MMKV
this.trim();
});
addMemoryWarningListener(this);
}

private get onValueChangedListeners() {
Expand Down
29 changes: 29 additions & 0 deletions package/src/MemoryWarningListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { AppState } from 'react-native';
import type { NativeEventSubscription } from 'react-native';
import { MMKVInterface } from './Types';

export function addMemoryWarningListener(mmkv: MMKVInterface): void {
if (global.WeakRef != null && global.FinalizationRegistry != null) {
// 1. Weakify MMKV so we can safely use it inside the memoryWarning event listener
const weakMmkv = new WeakRef(mmkv);
const listener = AppState.addEventListener('memoryWarning', () => {
// 0. Everytime we receive a memoryWarning, we try to trim the MMKV instance (if it is still valid)
weakMmkv.deref()?.trim();
});
// 2. Add a listener to when the MMKV instance is deleted
const finalization = new FinalizationRegistry(
(l: NativeEventSubscription) => {
// 3. When MMKV is deleted, this listener will be called with the memoryWarning listener.
l.remove();
}
);
// 2.1. Bind the listener to the actual MMKV instance.
finalization.register(mmkv, listener);
} else {
// WeakRef/FinalizationRegistry is not implemented in this engine.
// Just add the listener, even if it retains MMKV strong forever.
AppState.addEventListener('memoryWarning', () => {
mmkv.trim();
});
}
}

0 comments on commit eed7da4

Please sign in to comment.