Code
void _removeAt(int index) {
// The list holding the listeners is not growable for performances reasons.
// We still want to shrink this list if a lot of listeners have been added
// and then removed outside a notifyListeners iteration.
// We do this only when the real number of listeners is half the length
// of our list.
_count -= 1;
if (_count * 2 <= _listeners.length) {
final List<VoidCallback?> newListeners = List<VoidCallback?>.filled(_count, null);
// Listeners before the index are at the same place.
for (int i = 0; i < index; i++) {
newListeners[i] = _listeners[i];
}
// Listeners after the index move towards the start of the list.
for (int i = index; i < _count; i++) {
newListeners[i] = _listeners[i + 1];
}
_listeners = newListeners;
} else {
// When there are more listeners than half the length of the list, we only
// shift our listeners, so that we avoid to reallocate memory for the
// whole list.
for (int i = index; i < _count; i++) {
_listeners[i] = _listeners[i + 1];
}
_listeners[_count] = null;
}
}
Framework 代码总是性能取向的,Fixed List 可以尽可能的降低List的扩容机制浪费的内存空间。
tip:
- List<VoidCallback?>.filled(_count, null)
- keep index of listener: 使用截断和copy,避免普通removeAt只有_closeGap,未优化内存使用
- 最后的
_listeners[_count] = null左移至后清除引用
// Otherwise we put all the null references at the end.
for (int i = 0; i < newLength; i += 1) {
if (_listeners[i] == null) {
// We swap this item with the next not null item.
int swapIndex = i + 1;
while (_listeners[swapIndex] == null) {
swapIndex += 1;
}
_listeners[i] = _listeners[swapIndex];
_listeners[swapIndex] = null;
}
}
tip:
- 遍历到被移除的
Listener, 开始swap后边不为null的index,并将swapIndex _reentrantlyRemovedListeners = 0;单线程模型,仍旧需要一些手段同步数据
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {}
tip:
ValueNotifier<T>通过继承拥有了ChangeNotifier,拥有注册和管理Listener的能力,实现ValueListenable<T>拥有管理数据的抽象能力, 这种定义方式写法和写法习惯Java有一些区别,更像Rust的Struct和Impl分离的形式,提高了抽象能力,易于组合。
class _MergingListenable extends Listenable {
_MergingListenable(this._children);
final List<Listenable?> _children;
@override
void addListener(VoidCallback listener) {
for (final Listenable? child in _children) {
child?.addListener(listener);
}
}
@override
void removeListener(VoidCallback listener) {
for (final Listenable? child in _children) {
child?.removeListener(listener);
}
}
@override
String toString() {
return 'Listenable.merge([${_children.join(", ")}])';
}
}
这个类,被从继承ChangeNotifier 转换为Listenable, 提交记录为Fix Listenable.merge to not leak, 这个内存泄漏可能会被触发当多次调用removeListener, 系统代码还是很难写,测试用例发现了这个问题。
tip
- Listenable.merge, 在需要相应多个监听时很有用,React 的基础应该就是 Listenable。