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.mzph.cn/bicheng/28886.shtml

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

相关文章

Linux——ansible里的变量

在ansible里&#xff0c;变量干嘛用的 本身&#xff0c;ansible就是致力于&#xff0c;用尽可能“通用”的剧本&#xff0c;干所有场合的工作…… ansible里的变量怎么写 字母开头&#xff0c;包括&#xff1a;字母数字下划线 变量怎么定义&#xff08;声明&#xff09; 1.…

跟TED演讲学英文:Entertainment is getting an AI upgrade by Kylan Gibbs

Entertainment is getting an AI upgrade Link: https://www.ted.com/talks/kylan_gibbs_entertainment_is_getting_an_ai_upgrade Speaker: Kylan Gibbs Date: April 2024 文章目录 Entertainment is getting an AI upgradeIntroductionVocabularySummaryTranscriptAfterwor…

Ubuntu如何添加用户环境变量

一&#xff0c;简介 在工作中&#xff0c;需要将某个环境变量添加到用户环境变量中&#xff0c;方便使用。 要将 SOF_WORKSPACE~/work/sof 添加到用户的环境变量中&#xff0c;需要将该设置添加到用户的 shell 配置文件中&#xff0c;例如 ~/.bashrc&#xff08;对于 Bash 用…

【会议征稿】第五届物联网、人工智能与机械自动化国际学术会议 (IoTAIMA 2024,7月19-21)

由浙江工业大学主办&#xff0c;第五届物联网、人工智能与机械自动化国际学术会议 (IoTAIMA 2024) 将于2024年7月19-21日在浙江杭州召开。 会议旨在为从事物联网、人工智能与机械自动化的专家学者、工程技术人员、技术研发人员提供一个共享科研成果和前沿技术&#xff0c;了解学…

20240617通过串口配置索尼SONY的HDMI OUT输出的8530机芯

20240617通过串口配置索尼SONY的HDMI OUT输出的8530机芯 2024/6/17 15:54 缘起&#xff1a;需要在RK3588开发板OK3588-C上使用SONY的8530机芯。特意熟悉8530的串口命令&#xff01; 目的&#xff1a;需要配置SONY的8530机芯为RGB输出&#xff0c;4K分辨率。 串口波特率&#x…

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

讲动人的故事,写懂人的代码 5 Rust代码最常使用什么协议与其他代码交互? REST API: 2022年:51%2023年:51%看上去REST API的使用比例挺稳定的,没啥变化。语言互操作性(Language Interop): 2022年:53%2023年:43%语言互操作性的比例在2023年下来了一些,掉了10个百分点…

示例:WPF中DataGrid设置多级分组样式

一、目的&#xff1a;应用CollectionViewSource和GroupStyle设置DataGrid多级分组样式 二、实现 一级分组效果如下 二级分组效果如下 三、环境 VS2022 四、示例 具体实现代码如下 <TabItem Header"DataGrid - 多级分组"><TabItem.Resources><Colle…

ansible-Role角色批量按照node_export节点,并追加信息到Prometheus文件中

文章目录 剧本功能 inventory.yaml文件定义deploy.yaml角色定义node_exporter_lock角色定义任务角色main.yamlnode_exporter_tasks.yml角色触发任务notifyextra_tasks.yml角色prometheus_node_config.j2模板文件 执行命令查看变量 剧本功能 功能1&#xff1a; 批量执行node_ex…

Java数据结构与算法(多重背包)

前言: 多重背包问题&#xff08;Multiple Knapsack Problem, MKP&#xff09;是背包问题的一个变种。在这种问题中&#xff0c;你有多个背包&#xff0c;每个背包都有一定的容量。你需要选择一些物品放入这些背包中&#xff0c;以使放入物品的总价值最大化。每个物品都有一个价…

【Nvidia+AI摄像头】面向机器人双目视觉相机

随着人工智能和机器人技术的不断发展&#xff0c;双目深度相机作为一种重要的传感器&#xff0c;正在被广泛应用于各种机器人系统中。双目深度相机作为机器人不可或缺的感知器件&#xff0c;其高精度深度信息为机器人提供环境感知、立体视觉、姿态识别等功能&#xff0c;让机器…

Flink 窗口函数

一、Window 概述 Flink 流式计算是一种被设计用于处理无限数据集的数据处理引擎&#xff0c;而无限数据集是指一种不断增长的本质上无限的数据集&#xff0c;而 window 是一种切割无线数据为有限块进行处理的手段。 二、Window 分类 Window 可以分为两类&#xff1a; Count…

程序员画图工具?那必然是你了!!【送源码】

作为一个程序员&#xff0c;画图是必不可少的技巧。当然此画图不是搞艺术&#xff0c;而是画各种架构图、流程图、泳道图以及各种示意图。 平时我不论是记笔记、写技术文章&#xff0c;还是工作中写文档&#xff0c;都需要配上各种各样的示意图。不管是帮助自己更好的掌握知识…

云动态摘要 2024-06-17

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

Thinkpad系列产品进入Bios并设置U盘启动

Thinkpad系列产品&#xff0c;进入Bios并设置U盘启动&#xff0c;常用于以下场景&#xff1a; 1. 安装操作系统。 通过U盘启动盘&#xff0c;用户可以在电脑无法从硬盘启动或需要重装系统时&#xff0c;将操作系统安装到电脑中。这种方法简单且有效&#xff0c;节省了时间并方便…

夏季家里粉尘螨虫满天飞?一招搞定!好用家用空气净化器品牌分享

每到夏季&#xff0c;是家中尘螨滋生的高发期。夏季无论是开窗通风还是关窗开空调&#xff0c;都很容易造成空气中的浮尘堆积&#xff0c;不注意卫生清洁&#xff0c;容易滋生细菌、尘螨。 易过敏、体质弱的人群长时间在空气污染环境中&#xff0c;很容易就会过敏或者发生其他…

Hive笔记-2

第 3 章 DDL (Data Definition Language) 数据定义 DDL数据定义语言 DML数据操作语言 3.1 数据库 (database) 3.1.1 创建数据库 1) 语法 CREATE DATABASE [IF NOT EXISTS] database_name [COMMENT database_comment] [LOCATION hdfs_path] [WITH DBPROPERTIES (property_…

Git 代码管理规范 !

分支命名 master 分支 master 为主分支&#xff0c;也是用于部署生产环境的分支&#xff0c;需要确保master分支稳定性。master 分支一般由 release 以及 hotfix 分支合并&#xff0c;任何时间都不能直接修改代码。 develop 分支 develop 为开发环境分支&#xff0c;始终保持最…

在微信公众号上怎么添加预定房间功能

在这个快节奏的现代社会&#xff0c;人们对于便捷与高效的需求日益增加。特别是在旅行或出差时&#xff0c;能够快速、方便地预订一间舒适的房间&#xff0c;无疑是每个人心中的小确幸。今天&#xff0c;我们为您带来了一项革命性的服务——微信公众号上的房间预定功能&#xf…

Serverless如何赋能餐饮行业数字化?乐凯撒思变之道

导语 | 在数字化浪潮席卷全球的今天&#xff0c;每一个行业都在经历着前所未有的变革。餐饮行业作为人们日常生活中不可或缺的一部分&#xff0c;更是面临着巨大的转型压力。如何完成数字化转型&#xff0c;打破传统经营模式的限制&#xff0c;成为摆在众多餐饮商家面前的一道难…

Windows 10永久关闭“系统准备工具 3.14“禁止开机自启

文章目录 一、问题描述二、解决方法总结 一、问题描述 每次开机都会显示如下图所示的 系统准备工具 3.14 二、解决方法 按win R键打开运行窗口 → 输入cmd → 点击 确定 如图所示输入下面如图所示代码 → 按 回车 → 输入 Y → 按 回车 XCOPY C:\windows\System32\svchost.e…