当前位置: 首页 > news >正文

Flutter中的Future和Stream

在 Flutter 中,FutureStream 都是用于处理异步操作的类,它们都基于 Dart 的异步编程模型,但是它们的使用场景和工作方式有所不同。以下是它们的区别以及各自适用的场景。

目录

    • 一、Future
      • 1、基本使用
      • 2、异常处理
        • 1. catchError
        • 2. onError
        • 3、`catchError` 和 `onError` 的区别
        • 4. 捕获多个错误
        • 5. 错误的传播
      • 总结
    • 二、Stream
      • 1、基本使用
      • 2、异常处理
        • 1. `onError` 处理器
        • 2. `try-catch` 语句
        • 3. 异常后流的状态
        • 4. 流的中断
        • 5. 恢复流
        • 6. 流异常的示例
      • 总结
    • 三、 结合使用 `Future` 和 `Stream`
    • 四、总结
      • 1、区别
      • 2、选择 `Future` 还是 `Stream`
    • 四、疑问
      • 1、Stream 流是按照顺序执行的吗?
        • 1. 顺序性
        • 2. 顺序的保证
      • 2、Stream async* 和 yield 的解释这三个必须要配套使用吗?,不适用 yield 行吗?
        • 1. `Stream` 与 `async*`
        • 2. `async*` 和 `yield` 必须配合使用吗?
        • 3. 不使用 `yield` 行不行?
        • 4. 如何使用 `async*` 生成异步数据流?
        • 总结

一、Future

1、基本使用

Future 是一个表示一个可能还未完成的异步操作的对象。它表示一个将来某个时间点会返回一个结果或错误的计算。

  • 特点
    • Future 代表的是一个 单次 异步操作。
    • 只能返回一次结果或错误,不会再有后续的值。
    • 在调用时,它会返回一个 Future 对象,可以通过 thenawait 等方法获取结果。
    • 如果操作失败,可以通过 catchErroronError 进行错误处理。
  • 使用场景
    • 当你需要等待一个单一的异步结果时,使用 Future
    • 比如从网络获取数据,执行一个数据库查询,或读取一个文件等一次性的操作。
  • 示例代码
// 使用 Future 的示例
Future<String> fetchData() async {// 模拟异步操作await Future.delayed(Duration(seconds: 2));return "Data fetched successfully!";
}void main() async {try {String data = await fetchData();print(data); // 输出 "Data fetched successfully!"} catch (e) {print("Error: $e");}
}

在上面的示例中,fetchData 返回一个 Future<String>,它表示一个将来完成的异步操作。使用 await 来等待这个操作完成并获得结果。

2、异常处理

1. catchError

catchError 是用于捕获和处理 Future 发生错误的一种方式。当 Future 执行失败时,它会触发传给 catchError 的回调函数。这个回调函数可以接受错误和栈跟踪信息。

示例:

Future<int> divide(int a, int b) {return Future.delayed(Duration(seconds: 1), () {if (b == 0) {throw Exception('Cannot divide by zero!');}return a ~/ b;});
}void main() {divide(10, 0).catchError((e) {print('Error: $e');});// Output: Error: Exception: Cannot divide by zero!
}

在这个例子中,当 b 为 0 时,Future 会抛出一个 Exception,并且 catchError 捕获并打印这个错误。

catchError 的用法细节:

  • 返回值的传递catchError 会继续执行 Future 链中的后续操作,因此如果你想在错误发生时返回一个默认值,可以在 catchError 中指定。
Future<int> divide(int a, int b) {return Future.delayed(Duration(seconds: 1), () {if (b == 0) {throw Exception('Cannot divide by zero!');}return a ~/ b;}).catchError((e) {print('Handled error: $e');return -1;  // 返回默认值});
}void main() async {var result = await divide(10, 0);print('Result: $result');  // 输出: Handled error: Exception: Cannot divide by zero!//         Result: -1
}
2. onError

onErrorFuture 的另一种错误处理方式。它与 catchError 类似,但它是 Future 构造函数的一部分,通常用于直接在 Future 构造时附加错误处理。

示例:

Future<int> divide(int a, int b) {return Future.delayed(Duration(seconds: 1), () {if (b == 0) {throw Exception('Cannot divide by zero!');}return a ~/ b;}).onError((error, stackTrace) {print('Caught an error: $error');return -1;  // 返回默认值});
}void main() async {var result = await divide(10, 0);print('Result: $result');  // 输出: Caught an error: Exception: Cannot divide by zero!//         Result: -1
}
3、catchErroronError 的区别
  • catchError 是用于捕获在 Future 执行时抛出的异常,它通常用于链式调用中捕获错误。
  • onErrorFuture 的一种附加错误处理机制,它将错误处理直接嵌入到 Future 构造中。

尽管 catchErroronError 都可以捕获错误并返回一个默认值或执行某些操作,但 catchError 更灵活,通常在复杂的异步链式操作中使用。

4. 捕获多个错误

如果你需要捕获多个错误,可以将 catchErroronError 绑定到多个 Future 链条上。这样可以对不同类型的错误进行不同的处理。

示例:

Future<void> asyncFunction() {return Future.delayed(Duration(seconds: 1), () {throw Exception('Something went wrong!');});
}void main() {asyncFunction().catchError((e) {print('Caught error: $e');}).catchError((e) {print('Another handler for errors: $e');});// Output: Caught error: Exception: Something went wrong!
}
5. 错误的传播

如果在 Future 中没有处理错误,错误将会被传播,直到被外部捕获或程序崩溃。因此,适当的错误处理不仅可以帮助捕获问题,还可以避免未捕获的异常导致程序崩溃。

总结

在 Dart 中,catchErroronError 都可以用于处理异步操作中的错误。它们的主要区别在于用法和灵活性,选择哪一个取决于你的代码结构和需求。

二、Stream

1、基本使用

Stream 是一个表示一系列异步事件的对象,它允许你在未来的时间点接收多个值。

  • 特点
    • Stream 代表的是 多个异步事件
    • 它会按顺序提供一系列的结果(可以是零个或多个),通常用于处理实时数据流。
    • 可以是单向的,也可以是广播流(多个监听者可以订阅)。
    • 你可以通过 listen 方法来监听事件流。
    • Stream 还支持 await for 语法,可以等待并处理每个事件。
  • 使用场景
    • 当你需要处理一个 数据流多个值 时,使用 Stream
    • 比如处理实时数据(如 WebSocket 数据流、用户输入事件流、文件变化等)。
  • 示例代码
// 使用 Stream 的示例
Stream<int> generateNumbers() async* {for (int i = 0; i < 5; i++) {await Future.delayed(Duration(seconds: 1));yield i; // 每秒产生一个数字}
}void main() async {await for (var number in generateNumbers()) {print(number); // 输出:0, 1, 2, 3, 4}
}

在这个示例中,generateNumbers 返回一个 Stream<int>,它每秒返回一个整数。通过 await for 循环,我们可以逐个接收流中的数据。

2、异常处理

当流中发生异常时,有几种方式来处理这些异常,使得流能够继续工作或适当地终止。

1. onError 处理器

如果你使用 Stream.listen 方法来监听流,可以通过传入 onError 回调来处理流中的异常。当流抛出异常时,onError 处理器会被触发。

Stream<int> generateNumbersWithError() async* {yield 1;yield 2;throw Exception('Something went wrong');yield 3;  // 这一行永远不会执行
}void main() {generateNumbersWithError().listen((data) {print('Received: $data');},onError: (error) {print('Caught error: $error');},onDone: () {print('Stream is done');},);
}

输出:

Received: 1
Received: 2
Caught error: Exception: Something went wrong
Stream is done

在这个例子中,当 Stream 中的 Exception 被抛出时,onError 回调会捕获并打印出错误信息。yield 3 后的代码不会执行,因为流在抛出异常后被中断。

2. try-catch 语句

在异步生成器(如 async*)中,你可以使用 try-catch 来捕获异常,这可以防止异常导致流中断。

Stream<int> generateNumbersWithErrorHandled() async* {try {yield 1;yield 2;throw Exception('Something went wrong');yield 3;  // 这一行不会执行} catch (e) {print('Caught error: $e');}
}void main() async {await for (var data in generateNumbersWithErrorHandled()) {print('Received: $data');}
}

输出:

Received: 1
Received: 2
Caught error: Exception: Something went wrong

在这个例子中,即使抛出异常,Stream 仍然能够继续执行,只是异常会被捕获并处理。

3. 异常后流的状态

Stream 抛出异常后,流会进入错误状态,并且不再发出任何数据,除非你有合适的机制来恢复流。

  • 如果流中的 onError 回调没有捕获异常,流会直接终止。
  • 如果你在 Stream 中使用 try-catch 捕获了异常,流可以继续正常工作,继续发送后续的数据。
4. 流的中断

流的中断意味着流不再继续发出事件。中断的原因通常有以下几种:

  • 异常抛出:如果流中的某个操作抛出了异常,流会被中断,后续的事件不会再触发。
  • 用户主动取消订阅:如果你使用 StreamSubscription 来订阅流,并主动调用 cancel(),流也会中断。
  • 流结束:如果流完成(即没有更多的事件要发出),流会进入完成状态。
5. 恢复流

如果你希望在流发生异常后恢复流的工作,可以通过重新订阅流或使用一些复合的错误处理机制。

例如,在监听流时使用 onError 捕获错误并在错误发生时重新启动流:

Stream<int> generateNumbersWithError() async* {yield 1;yield 2;throw Exception('Something went wrong');yield 3;
}void main() {Stream<int> stream = generateNumbersWithError();stream.listen((data) {print('Received: $data');},onError: (error) {print('Caught error: $error');// 重新启动流stream.listen((data) => print('Retry received: $data'),onError: (e) => print('Retry error: $e'),onDone: () => print('Retry stream is done'),);},onDone: () => print('Stream is done'),);
}

在这种情况下,流会在错误发生时重新启动。这允许你捕获错误并尝试恢复流的执行。

6. 流异常的示例

假设有一个 Stream 生成器,它在生成某个事件时发生异常:

Stream<int> generateNumbersWithError() async* {yield 1;yield 2;throw Exception('Unexpected error');yield 3;  // 这行永远不会执行
}void main() async {try {await for (var number in generateNumbersWithError()) {print('Received: $number');}} catch (e) {print('Caught error: $e');}
}

输出:

Received: 1
Received: 2
Caught error: Exception: Unexpected error

在这个例子中,异常会导致流中断,后续的事件不会被处理,且异常被捕获。

总结

  • 流中断:当流遇到异常时,流会进入错误状态并停止发出事件。
  • 异常处理:你可以通过 onError 回调或 try-catch 语句捕获和处理异常。
  • 恢复流:在流发生异常时,可以选择恢复流的工作,例如通过重新订阅流。

三、 结合使用 FutureStream

在某些情况下,FutureStream 可以结合使用。例如,如果你有一个 Future 返回一个数据集,而这个数据集可以被逐步处理,那么你可以将 Future 的结果转换成一个 Stream 来进行逐项处理。

Future<List<int>> fetchData() async {return [1, 2, 3, 4, 5];
}Stream<int> fetchDataAsStream() async* {List<int> data = await fetchData();for (var item in data) {yield item;}
}void main() async {await for (var number in fetchDataAsStream()) {print(number); // 输出 1, 2, 3, 4, 5}
}

在这个例子中,fetchData 是一个 Future,而 fetchDataAsStream 将其转换成了一个 Stream,使得我们能够以流的形式逐项处理数据。

总结

  • Future 适合用于处理 单次 的异步操作,返回一个值或错误。
  • Stream 适合用于处理 多次 的异步事件或数据流,允许你持续接收多个值。

了解它们的区别和使用场景可以帮助你更好地选择异步操作的方式,从而提高代码的可读性和性能。

四、总结

1、区别

特性FutureStream
返回值只返回一个值或一个错误持续返回多个值或错误
生命周期一次性操作,完成后不再有新的值持续发出多个值(或者是事件)
异步操作适用于单次异步操作适用于多次异步事件或数据流
操作方式thencatchErrorawaitlistenawait foraddaddError

2、选择 Future 还是 Stream

  • 使用 Future
    • 你在处理一个单次的异步操作时。
    • 比如从网络获取数据、计算结果、执行数据库操作等。
    • 示例:登录请求、获取单个 API 响应、读取文件内容。
  • 使用 Stream
    • 当你需要处理 多个异步事件,例如实时数据流或变化时。
    • 比如 WebSocket 消息、实时位置更新、文件读取(大文件分片)等。
    • 示例:实时聊天消息、连续的数据更新、传感器数据流等。

四、疑问

1、Stream 流是按照顺序执行的吗?

是的,Stream 在 Dart 中是 按顺序 执行的。具体来说,Stream 中的事件(数据或错误)会按照它们被 产生发出 的顺序进行传递和处理。

1. 顺序性

在 Dart 中,Stream 会按照 事件发出的顺序 将这些事件传递给订阅者。也就是说,先发出的事件会先被监听器处理。

例如,如果你有一个 Stream 生成数据流,并且在每个数据项之间有延时,那么监听器将会按照数据产生的顺序处理每个事件。

示例代码:顺序执行的 Stream

Stream<int> generateNumbers() async* {for (int i = 0; i < 3; i++) {await Future.delayed(Duration(seconds: 1));  // 模拟延时yield i;  // 按顺序发出数据}
}void main() async {await for (var number in generateNumbers()) {print('Received: $number');  // 按顺序输出}
}

输出:

Received: 0
Received: 1
Received: 2

在这个例子中,数据流是按顺序传递给监听器的,即 012 按照生成的顺序被逐个输出。

2. 顺序的保证

Stream 本身的设计保证了事件的顺序。无论你是通过 await for 或者 listen 来处理事件,事件会按照发出顺序依次传递给你。

通过 listen 监听的顺序

Stream<int> generateNumbers() async* {for (int i = 0; i < 3; i++) {await Future.delayed(Duration(seconds: 1));yield i;}
}void main() {generateNumbers().listen((data) {print('Received: $data');  // 按顺序输出});
}

这段代码会输出:

Received: 0
Received: 1
Received: 2

如上所示,数据按照顺序传递。

2、Stream async* 和 yield 的解释这三个必须要配套使用吗?,不适用 yield 行吗?

在 Dart 中,Streamasync*yield 是紧密关联的,但它们不一定是必须同时使用的。它们之间的关系可以分开解释,看看如何组合使用,或者是否可以在没有 yield 的情况下使用 async*

1. Streamasync*

Stream 是 Dart 中用于处理异步数据流的核心概念。Stream 对象用于处理一系列异步事件,而 async* 是定义异步生成器(异步迭代器)的一种语法。async* 允许你生成一个 Stream,并通过 yield 来发出数据。

  • async* 标识一个异步生成器函数,它返回一个 Stream
  • yield 用于在异步生成器中逐个发出数据。
2. async*yield 必须配合使用吗?
  • async*yield 一般是配套使用的,但你也可以只使用 async*,并没有强制要求一定要用 yield
  • 如果你不需要发出数据(即你不想使用 yield),你也可以用 async* 作为一个简单的异步函数来返回一个空的 Stream,或者使用 await 来发出异步的结果。
3. 不使用 yield 行不行?

是的,可以在没有 yield 的情况下使用 async*,但通常这样做的结果是流不会发出任何数据。在这种情况下,Stream 会是一个空的流,或者说它在没有任何数据的情况下完成。

示例 1:不使用 yield,生成一个空的流

Stream<int> generateEmptyStream() async* {// 什么都不发出
}void main() async {await for (var value in generateEmptyStream()) {print(value);  // 这里不会有任何输出}
}

在这个例子中,async* 只是声明了一个异步生成器,但是没有 yield,因此返回的 Stream 是空的,不会有任何数据输出。

4. 如何使用 async* 生成异步数据流?

async* 用于返回 Stream,可以通过 yield 来逐个发出数据。你也可以结合 await 来进行异步操作后再发出数据。这是一个典型的用法:

Stream<int> generateNumbers() async* {for (int i = 1; i <= 5; i++) {await Future.delayed(Duration(seconds: 1));  // 模拟异步操作yield i;  // 发出数据}
}void main() async {await for (var number in generateNumbers()) {print(number);}
}

输出:

1
2
3
4
5

在这个例子中,async* 通过 yield 发出了多个数字,每次发出时都延迟 1 秒。

总结
  • async*yield 通常一起使用来生成和发出异步数据流。
  • 不一定非要有 yield (但不使用 yield 意义不大),但如果你希望生成一个有数据的流,就需要使用 yield 或其他发出数据的方式(例如 yield*)。
  • 如果你在 async* 中没有 yield,返回的 Stream 将不会发出任何数据,通常这种情况用于创建空流或只执行异步操作的函数。

因此,虽然 async*yield 是紧密相关的,但它们不必总是同时使用。如果不使用 yield,可以生成一个空流或执行异步操作,但不会有数据发出。

相关文章:

Flutter中的Future和Stream

在 Flutter 中&#xff0c;Future 和 Stream 都是用于处理异步操作的类&#xff0c;它们都基于 Dart 的异步编程模型&#xff0c;但是它们的使用场景和工作方式有所不同。以下是它们的区别以及各自适用的场景。 目录 一、Future1、基本使用2、异常处理1. catchError2. onError…...

107.【C语言】数据结构之二叉树求总节点和第K层节点的个数

目录 1.求二叉树总的节点的个数 1.容易想到的方法 代码 缺陷 思考:能否在TreeSize函数内定义静态变量解决size的问题呢? 其他写法 运行结果 2.最好的方法:分而治之 代码 运行结果 2.求二叉树第K层节点的个数 错误代码 运行结果 修正 运行结果 其他写法 1.求二…...

spring boot支持那些开发工具?

Spring Boot 支持多种开发工具&#xff0c;以帮助开发者更高效地进行应用开发。以下是小编给大家分享几种常用的开发工具及其特点&#xff1a; IntelliJ IDEA&#xff1a; IntelliJ IDEA 是一款非常流行的 Java IDE&#xff0c;它提供了对 Spring Boot 的全面支持&#xff0c;…...

Go-MediatR:Go语言中的中介者模式

在Go语言中&#xff0c;确实存在一个与C#中的MediatR类似的组件包&#xff0c;名为Go-MediatR。 Go-MediatR是一个受.NET中MediatR库启发的Go语言实现&#xff0c;它专注于通过中介者模式简化命令查询责任分离&#xff08;CQRS&#xff09;模式的处理和在事件驱动架构中的应用…...

5.11【机器学习】

先是对图像进行划分 划分完后&#xff0c; 顺序读取文件夹&#xff0c;在文件夹里顺序读取图片&#xff0c; 卷积层又称为滤波器&#xff0c;通道是说滤波器的个数&#xff0c;黑白通道数为1&#xff0c;RGB通道个数为3 在输入层&#xff0c;对于输入层而言&#xff0c;滤波…...

在 CentOS 上安装 Docker:构建容器化环境全攻略

一、引言 在当今的软件开发与运维领域&#xff0c;Docker 无疑是一颗璀璨的明星。它以轻量级虚拟化的卓越特性&#xff0c;为应用程序的打包、分发和管理开辟了崭新的高效便捷之路。无论是开发环境的快速搭建&#xff0c;还是生产环境的稳定部署&#xff0c;Docker 都展现出了…...

Python练习(2)

重复元素判定续。利用集合的无重复性来编写一个程序如果有一个元素出现了不止一次则返回true但不要改变原来列表的值&#xff1a; 一&#xff1a; def has_duplicates(lst): # 使用集合来存储已经见过的元素 seen set() for item in lst: if item in seen: # 如果元素已经在…...

如何实现一套键盘鼠标控制两台计算机(罗技Options+ Flow功能快速实现演示)

需求背景 之前我写过一篇文章如何实现一套键盘鼠标控制两台计算机&#xff08;Mouse Without Borders快速上手教程&#xff09;_一套键鼠控制两台电脑-CSDN博客 当我们在局域网内有两台计算机&#xff0c;想使用一套键鼠操控时&#xff0c;可以安装Mouse Without Borders软件…...

现代应用程序中基于 Cell 架构的安全防护之道

在飞速发展的软件开发领域&#xff0c;基于 Cell 的架构日益流行起来。其概念源自船舶舱壁的设计准则&#xff0c;即单独的水密舱室能允许故障孤立存在。通过将这个概念应用于软件&#xff0c;我们创建了一个架构&#xff0c;将应用程序划分为离散的、可管理的组件&#xff0c;…...

【导航查询】.NET开源 ORM 框架 SqlSugar 系列

.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…...

【基础分析】——Qt 信号和槽的机制 优点

QT信号和槽机制的优点包括&#xff1a; 1、类型安全&#xff1a; 信号和槽的签名必须是等同的&#xff0c;即信号的参数类型和参数个数必须与接收该信号的槽的参数类型和参数个数相同。 2、松散耦合&#xff1a; 信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无须知道…...

Vue3学习宝典

1.ref函数调用的方式生成响应式数据&#xff0c;可以传复杂和简单数据类型 <script setup> // reactive接收一个对象类型的数据 import { reactive } from vue;// ref用函数调用的方式生成响应式数据&#xff0c;可以传复杂和简单数据类型 import { ref } from vue // 简…...

leecode96.不同的二叉搜索树

在画的过程中发现规律&#xff0c;每次选择不同的节点作为根节点&#xff0c;左右两边的节点再排列组合一下就能求出总数 class Solution { public:int numTrees(int n) {vector<int> dp(n1,0);dp[0]1;for(int i1;i<n;i)for(int j0;j<i;j)dp[i]dp[i-j-1]*dp[j];ret…...

树莓派基本配置-基础配置配置

树莓派基本配置 文章目录 树莓派基本配置前言硬件准备树莓派刷机串口方式登录树莓派接入网络ssh方式登录树莓派更换国内源xrdp界面登录树莓派远程文件传输FileZilla 前言 树莓派是一款功能强大且价格实惠的小型计算机&#xff0c;非常适合作为学习编程、物联网项目、家庭自动化…...

手机卡限速丨中国移动5G变3G,网速500kb

以下猜测错误&#xff0c;又有新的猜测&#xff1a;河南移动的卡出省限速。可能是因为流量结算。 “2024年7月1日起&#xff0c;中国移动集团内部将开启跨省流量结算” 在深圳四五年了&#xff0c;之前没有过&#xff0c;就从上个月开始。11月底解除限速&#xff0c;12月刚开…...

SpringCloud之OpenFeign:OpenFeign与Feign谁更适合你的SpringCloud项目?

目录 一、OpenFeign简介1、OpenFeign是什么&#xff08;1&#xff09;核心概念&#xff08;2&#xff09;工作原理&#xff08;3&#xff09;主要特点&#xff08;4&#xff09;使用场景&#xff08;5&#xff09;与Feign的区别&#xff08;6&#xff09;总结 2、OpenFeign与Fe…...

yt6801 ubuntu有线连接驱动安装

耀世16pro的有线网卡驱动安装 下载地址: YT6801 千兆PCIE以太网控制器芯片 1. 创建安装目录 mkdir yt68012. 解压驱动文件 unzip yt6801-linux-driver-1.0.27.zip -d yt68013. 进入驱动目录 cd yt68014. 安装驱动 以 root 权限运行安装脚本&#xff1a; sudo su ./yt_ni…...

算法日记 36-38day 动态规划

今天把动态规划结束掉&#xff0c;包括子序列以及编辑距离 题目&#xff1a;最长公共子序列 1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &…...

hdlbits系列verilog解答(Dff16e-同步复位上升沿16位触发器)-85

文章目录 一、问题描述二、verilog源码三、仿真结果一、问题描述 本节学习如何创建16位D触发器。有时仅修改一组触发器一部分是有用的。字节使能控制16位寄存器的哪一个字节应当被修改,其中teena[1]控制高位字节[15:8],teena[0]控制低位字节[7:0]。restn是一个同步低电平有效…...

HTTPTomcatServlet

今日目标: 了解JavaWeb开发的技术栈理解HTTP协议和HTTP请求与响应数据的格式掌握Tomcat的使用掌握在IDEA中使用Tomcat插件理解Servlet的执行流程和生命周期掌握Servlet的使用和相关配置1,Web概述 1.1 Web和JavaWeb的概念 Web是全球广域网,也称为万维网(www),能够通过浏览…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

软件工程 期末复习

瀑布模型&#xff1a;计划 螺旋模型&#xff1a;风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合&#xff1a;模块内部功能紧密 模块之间依赖程度小 高内聚&#xff1a;指的是一个模块内部的功能应该紧密相关。换句话说&#xff0c;一个模块应当只实现单一的功能…...

ArcGIS Pro+ArcGIS给你的地图加上北回归线!

今天来看ArcGIS Pro和ArcGIS中如何给制作的中国地图或者其他大范围地图加上北回归线。 我们将在ArcGIS Pro和ArcGIS中一同介绍。 1 ArcGIS Pro中设置北回归线 1、在ArcGIS Pro中初步设置好经纬格网等&#xff0c;设置经线、纬线都以10间隔显示。 2、需要插入背会归线&#xf…...

写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里

写一个shell脚本&#xff0c;把局域网内&#xff0c;把能ping通的IP和不能ping通的IP分类&#xff0c;并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...