Dart 弱引用进阶

前言

村里的老人说:“真正的强者,都是扮猪吃老虎。

日常开发中经常需要用到弱引用,Dart 语言里也有提供弱引用的接口 ```WeakReference```,我们可以基于它开发更强大的复杂结构。

在前面的文章中,我们用到了一个以弱引用对象为元素的集合 ```WeakSet```,今天我们就来讲一下它是如何实现的。

Collections

常用的 collections 有 Set、List 和 Map,下面我将为大家介绍其对应的弱引用版本该如何快速实现。

WeakSet

弱集合也是集合,所以它应该实现一个集合接口 ```Set``` 的所有功能。

直接上代码:

class WeakSet<E extends Object> implements Set<E> {WeakSet() : _inner = {};final Set<WeakReference<E>> _inner;@overridevoid clear() => _inner.clear();@overrideSet<E> toSet() {Set<E> entries = {};E? item;for (WeakReference<E> wr in _inner) {item = wr.target;if (item != null) {entries.add(item);}}return entries;}@overrideList<E> toList({bool growable = true}) => toSet().toList(growable: growable);@overrideString toString() => toSet().toString();@overrideIterator<E> get iterator => toSet().iterator;@overrideint get length => toSet().length;@overridebool get isEmpty => toSet().isEmpty;@overridebool get isNotEmpty => toSet().isNotEmpty;@overridebool contains(Object? value) => toSet().contains(value);@overridebool containsAll(Iterable<Object?> other) => toSet().containsAll(other);@overrideE elementAt(int index) => toSet().elementAt(index);@overridebool add(E value) => !contains(value) && _inner.add(WeakReference(value));@overridebool remove(Object? value) {for (var wr in _inner) {if (wr.target == value) {return _inner.remove(wr);}}return false;}@overridevoid forEach(void Function(E element) action) => toSet().forEach(action);// 从略...}

首先,我们创建一个内置的集合 _inner,其元素是指向元素类型 E 的弱引用;
接下来实现 toSet() 函数,将 _inner 中所有实际指向的对象提取出来,构建一个新的集合返回;
然后其余的 Set 函数接口都可以基于这个 toSet() 来实现。

同样的思路,我们还可以继续实现列表与映射的弱引用版本。

WeakList

先实现最简单的那些接口函数:

class WeakList<E extends Object> implements List<E> {WeakList() : _inner = [];final List<WeakReference<E>> _inner;@overridevoid clear() => _inner.clear();@overrideList<E> toList({bool growable = true}) {List<E> entries = [];E? item;for (WeakReference<E> wr in _inner) {item = wr.target;if (item != null) {entries.add(item);}}return entries;}@overrideSet<E> toSet() => toList().toSet();@overrideString toString() => toList().toString();@overrideIterator<E> get iterator => toList().iterator;@overrideint get length => toList().length;@overridebool get isEmpty => toList().isEmpty;@overridebool get isNotEmpty => toList().isNotEmpty;@overrideE get first => toList().first;@overrideset first(E item) => _inner.first = WeakReference(item);@overrideE get last => toList().last;@overrideset last(E item) => _inner.last = WeakReference(item);@overrideE get single => toList().single;@overrideList<E> operator +(List<E> other) => toList() + other;@overrideE operator [](int index) => toList()[index];@overridevoid add(E value) => _inner.add(WeakReference(value));@overridevoid addAll(Iterable<E> iterable) {for (var item in iterable) {add(item);}}@overridebool contains(Object? element) => toList().contains(element);@overrideE elementAt(int index) => toList().elementAt(index);@overridevoid forEach(void Function(E element) action) => toList().forEach(action);@overrideint indexOf(E element, [int start = 0]) => toList().indexOf(element, start);@overrideint lastIndexOf(E element, [int? start]) => toList().lastIndexOf(element, start);@overrideString join([String separator = ""]) => toList().join(separator);@overridebool remove(Object? value) {/// Removes the first occurrence of [value] from this list.for (var wr in _inner) {if (wr.target == value) {return _inner.remove(wr);}}return false;}// 从略...}

跟上面的 WeakSet 类似,这里也是先创建一个内部的列表 _inner,其元素是指向实际对象的弱引用;接下来实现 toList() 函数,将内部列表中的实际指向对象提取出来构建新的列表;
然后其余关于 List 的接口函数基本都可以基于这个 toList() 函数来实现。

但是也有不同之处。由于 Set 是无序的,所以即使其中的弱引用所指向的对象已销毁,也不会影响集合运算。
而 List 是有序的,所以一些跟序号相关的操作则需要忽略那些对象已销毁的弱引用元素。
这里我采用的犯法是增加一个 purge() 函数,用于清除那些“无用”的元素,然后再基于它去实现那些和序号相关的接口函数。

class WeakList<E extends Object> implements List<E> {WeakList() : _inner = [];final List<WeakReference<E>> _inner;void purge() => _inner.removeWhere((wr) => wr.target == null);@overrideset length(int size) {purge();_inner.length = size;}@overridevoid operator []=(int index, E value) {purge();_inner[index] = WeakReference(value);}@overridevoid insert(int index, E element) {purge();_inner.insert(index, WeakReference(element));}@overridevoid insertAll(int index, Iterable<E> iterable) {purge();for (var item in iterable) {_inner.insert(index, WeakReference(item));index += 1;}}@overrideE removeAt(int index) {purge();var wr = _inner.removeAt(index);return wr.target;}@overrideE removeLast() {purge();var wr = _inner.removeLast();return wr.target;}// 从略...}

WeakValueMap

Talk is cheap, show you the codes!

class WeakValueMap<K, V> implements Map<K, V> {WeakValueMap() : _inner = {};final Map<K, WeakReference<dynamic>?> _inner;void purge() => _inner.removeWhere((key, wr) => wr?.target == null);@overridevoid clear() => _inner.clear();Map<K, V> toMap() {// remove empty entriespurge();// convert entriesreturn _inner.map((key, wr) => MapEntry(key, wr?.target));}@overrideString toString() => toMap().toString();@overridebool containsValue(Object? value) => toMap().containsValue(value);@overridebool containsKey(Object? key) => _inner[key]?.target != null;@overrideV? operator [](Object? key) => _inner[key]?.target;@overridevoid operator []=(K key, V value) =>_inner[key] = value == null ? null : WeakReference(value);@overrideIterable<MapEntry<K, V>> get entries => toMap().entries;@overridevoid addAll(Map<K, V> other) => other.forEach((key, value) {this[key] = value;});@overrideV? remove(Object? key) => _inner.remove(key)?.target;@overridevoid forEach(void Function(K key, V value) action) => _inner.forEach((key, wr) {V val = wr?.target;if (val != null) {action(key, val);}});@overrideIterable<K> get keys => toMap().keys;@overrideIterable<V> get values => toMap().values;@overrideint get length => toMap().length;@overridebool get isEmpty => toMap().isEmpty;@overridebool get isNotEmpty => toMap().isNotEmpty;// 从略...}

课余练习

  • WeakKeyMap 如何实现?

提示:可以先实现一个用于包裹 key 的类,以 key 对应的对象为参数初始化(内部用一个弱引用指向它),同时将 key 对象的 hash 值等信息保存起来,以便后面 key 对象消失时仍然可以进行相应的运算。

应用示例

以上类的使用很简单,根普通的 Set、List、Map 几乎没有差别:

// WeakSet
Set<Observer> listenerSet = WeakSet();
listenerSet.add(obs1);
listenerSet.add(obs2);// WeakList
List<Observer> listenerList = WeakList();
listenerList.add(obs1);
listenerList.add(obs2);// WeakMap
Map<String, Observer> listenerMap = WeakValueMap();
listenerMap['Albert'] = obs1;
listenerMap['Ben'] = obs2;

代码引用

由于我已将这部分代码提交到了 pub.dev,所以在实际应用中,你只需要在项目工程文件 ```pubspec.yaml``` 中添加:

dependencies:
    object_key: ^0.1.1

然后在需要使用的 dart 文件头引入即可:

import 'package:object_key/object_key.dart';

全部源码

GitHub 地址:

https://github.com/moky/ObjectKey/tree/main/object_key/lib/src

结语

弱引用可以有效避免对象循环引用等原因导致的内存泄漏问题。
在某些实践中,还可以实现类似“自动退出”的效果,比如前面介绍的观察者模式的实战中,合理使用基于弱引用的集合,可以达到当观察者销毁时即自动注销的效果,十分方便!

如有其他问题,可以下载登录 Tarsier 与我交流(默认通讯录里找 Albert Moky / 章北海)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/352084.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【笔记】【Git】多个dev分支合并到master分支的文件冲突

问题描述 多个dev分支在同步开发&#xff0c;同时发起代码评审&#xff0c;但合入master的时候存在先后顺序&#xff0c;那么后面同文件的操作则会提示“合并有文件冲突”&#xff0c;导致代码无法入库&#xff0c;只能重新提交。 在个人分支中如何解决与master分支差异&#…

香港优才计划中介避坑,深圳哪家优才中介有实力?

随着香港优才计划取消配额限制以来&#xff0c;优才计划递交申请量骤增&#xff0c;许多DIY的申请人在递交申请后&#xff0c;长时间未能收到审批结果&#xff0c;甚至有人等待了12个月之久仍对审批进展一无所知。 而一些有中介协助的申请人&#xff0c;在等待审批的过程中&am…

通过git命令查询某个用户提交信息

要查询某个用户通过 Git 提交了多少行代码&#xff0c;可以使用以下步骤和命令来实现。这些命令将统计该用户的添加和删除的代码行数。 1、切换到你的 Git 仓库&#xff1a; cd /path/to/your/repositorygit命令结果&#xff1a; 2、查询所有用户&#xff1a; git log --pr…

DPDK环境配置

DPDK环境配置 DPDK&#xff08;Data Plane Development Kit&#xff09;是一个开源的软件框架&#xff0c;最初由Intel开发&#xff0c;旨在提升数据包处理性能&#xff0c;尤其是在Intel架构的处理器上。它允许开发者在用户空间&#xff08;user space&#xff09;而不是传统…

github国内加速访问有效方法

这里只介绍实测最有效的一种方法&#xff0c;修改主机的Hosts文件&#xff0c;如果访问github网站慢或者根本无法访问的时候可以采用下面方法进行解决。 1、搜索一个IP查询网站 首先百度搜索选择一个IP查询的网站&#xff0c;这里我用下面这个网站&#xff08;如果该网站失效…

重新安装TortoiseGit后提示权限错误问题解决

今天在Windows11系统中下载安装使用TortoiseGit可视化Git工具&#xff0c;进行代码提交管理。 由于电脑之前是一位开发人员在使用&#xff0c;所以曾经安装使用过这个工具。 重新安装好软件后&#xff0c;在coding网站中复制代码路径后&#xff0c;在本地目录通过鼠标右键选择…

电影《加菲猫家族》观后感

上周看了电影《加菲猫家族》&#xff0c;本片其中有很多明亮的画面&#xff0c;相关艳丽的色彩&#xff0c;充满温馨的场景&#xff0c;很符合加菲猫的一贯画风&#xff0c;即使反派出场时&#xff0c;带有阴暗的感觉&#xff0c;看起也不是特别吓人&#xff0c;比较欢乐气氛&a…

找不到com.fasterxml.jackson.core.exc.StreamWriteException的类文件

1. 前言: 使用springboot搭建的项目, 需要使用 jackson 更改json文件的内容; maven管理jar包, 导入jar包版本信息如下: <!-- 读写json文件所需依赖 --> <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databin…

数据中台-知识图谱平台

【数据分析小兵】专注数据中台产品领域,覆盖开发套件,包含数据集成、数据建模、数据开发、数据服务、数据可视化、数据治理相关产品以及相关行业的技术方案的分享。对数据中台产品想要体验、做二次开发、关注方案资料、做技术交流的朋友们&#xff0c;可以关注我。 1. 概述 随着…

[element-ui]el-select多选选择器选中其中一个选项,不可删除

背景&#xff1a; 产品真的很多奇奇怪怪的需求&#xff0c;一边吐槽一边实现。 前提&#xff1a;选择器作为表格的筛选项&#xff0c;提供三个选项值。 要求&#xff1a;默认选中其中一个值&#xff0c;这个值不可删除。 如图&#xff1a; 小声吐槽&#xff1a;搞这些有什么…

样本学习:当AI遇上“少见多怪”

东汉名臣牟融在其著作《牟子》写道&#xff1a;“少所见&#xff0c;多所怪&#xff0c;睹橐驼&#xff0c;谓马肿背。”意思是见闻少的人遇到不常见的事物就觉得奇怪&#xff0c;见到骆驼也以为是背肿了的马。因此&#xff0c;后人总用“少见多怪”来嘲笑见识浅陋的人。然而&a…

Ps:脚本事件管理器

Ps菜单&#xff1a;文件/脚本/脚本事件管理器 Scripts/Script Events Manager 脚本事件管理器 Script Events Manager允许用户将特定的事件&#xff08;如打开、存储或导出文件&#xff09;与 JavaScript 脚本或 Photoshop 动作关联起来&#xff0c;以便在这些事件发生时自动触…

Excel和Word等工具小技能分享汇编(一)

这里汇集刘小生前期微信公众号分享的Excel和Word等工具小技能&#xff0c;为方便大家查看学习&#xff0c;刘小生对其进行分类整理&#xff0c;后期也会不定期整理更新&#xff0c;如有想学习交流或其他小技巧需求&#xff0c;欢迎留言&#xff0c;我们一起学习进步&#xff01…

云动态摘要 2024-06-17

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 [低至1折]腾讯混元大模型产品特惠 腾讯云 2024-06-06 腾讯混元大模型产品特惠&#xff0c;新用户1折起&#xff01; 云服务器ECS试用产品续用 阿里云 2024-04-14 云服务器ECS试用产品续用…

B端业务需求分析的3大注意事项

通过深入分析业务需求&#xff0c;可以准确理解B端用户的具体需求&#xff0c;帮助项目团队设计出真正解决企业问题、提高工作效率的产品或服务。这减少了后期变更&#xff0c;节约了时间和资源。如果没有深入分析业务需求&#xff0c;产品或服务功能可能与实际业务需求脱节&am…

Java家政预约系统源码 家政上门APP源码 家电安装、维修、清洗、美容系统源码、家政系统各端功能细分

Java家政预约系统源码 家政上门APP源码 家电安装、维修、清洗、美容系统源码、家政系统各端功能细分 家政服务系统是一种提供家政服务的系统&#xff0c;它可以为客户提供上门家庭清洁、钟点工、保姆、月嫂、育婴师、护理员等家政服务。节省时间和成本&#xff0c;提高效率&…

「网络原理」IP 协议

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;计网 &#x1f387;欢迎点赞收藏加关注哦&#xff01; IP 协议 &#x1f349;报头结构&#x1f349;地址管理&#x1f34c;动态分配 IP 地址&#x1f34c;NAT 机制&#xff08;网络地址映射&am…

从11个视角看全球Rust程序员1/4:深度解读JetBrains最新报告

讲动人的故事,写懂人的代码 五个月前,编程界的大佬JetBrains发布了他们的全球开发者年度报告。 小吾从这份报告中找出了下面11个关于全球程序员如何使用Rust的有趣的趋势,让你学习和使用Rust更轻松。 1 这两年有多少程序员在工作中使用了Rust? 2 全球程序员使用Rust有多…

一套上门家政小程序源码,java上门家政app源码,家政服务管理后台系统源代码

一套上门家政小程序源码&#xff0c;上门家政app源码&#xff0c;家政服务管理后台系统源码 家政上门系统开发端口组成&#xff1a;用户端app技工端apppc管理后台&#xff1a;Uniapp开发&#xff0c;系统可以打包&#xff1a;安卓、ios、h5、微信小程序、公众号。 一、用户端&…

PHP邮箱服务器搭建与配置教程?如何使用?

PHP邮箱服务器搭建的步骤&#xff1f;服务器搭建的注意事项&#xff1f; 在当今的数字化时代&#xff0c;电子邮件仍然是沟通和业务处理的重要工具之一。通过PHP搭建和配置一个邮箱服务器&#xff0c;您可以实现自主掌控邮件系统&#xff0c;确保数据的安全性和隐私性。AokSen…