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

Swift 宏(Macro)入门趣谈(五)

在这里插入图片描述

概述

苹果在去年 WWDC 23 中就为 Swift 语言新增了“其利断金”的重要小伙伴 Swift 宏(Swift Macro)。为此,苹果特地用 2 段视频(入门和进阶)颇为隆重的介绍了它。

在这里插入图片描述

那么到底 Swift 宏是什么?有什么用?它和 C/C++ 语言中的宏又有什么异同呢?本系列博文将会尝试为小伙伴们揭开 Swift 宏的神秘面纱。

在本篇博文中,您将学到如下内容:

  • 概述
    • 5.4 完成宏主体
    • 5.5 验证宏展开后的结果
  • 6. 另一个简洁的解决方案
  • 7. 目前 Swift 宏的不足之处
  • 总结

相信学完本系列博文后,Swift Macro 会从大家心中的“阳春白雪”变为“阳阿薤露”,小伙伴们必可以将它们运用的“如臂使指”。

那还等什么呢?Let‘s go!!!😉


5.4 完成宏主体

在完成了 @nilable 宏接口、客户端代码以及宏主体初步结构的搭建之后,现在回到 NilableMacro 结构的定义中,我们现在可以尝试补全 expansion() 方法中所有展开代码了。

不过在撸码之前,我们有必要先来了解一下 expansion() 方法传入实参所扮演的角色。在这个例子中,我们只关心其中的 declaration 参数,它是一个 some DeclSyntaxProtocol 类型:

providingPeersOf declaration: some DeclSyntaxProtocol

在 Xcode 调试控制台中,我们利用 po 命令可以列出它的结构细节:

FunctionDeclSyntax
├─attributes: AttributeListSyntax
│ ╰─[0]: AttributeSyntax
│   ├─atSign: atSign
│   ╰─attributeName: IdentifierTypeSyntax
│     ╰─name: identifier("nilable")
├─modifiers: DeclModifierListSyntax
│ ├─[0]: DeclModifierSyntax
│ │ ╰─name: keyword(SwiftSyntax.Keyword.static)
│ ╰─[1]: DeclModifierSyntax
│   ╰─name: keyword(SwiftSyntax.Keyword.private)
├─funcKeyword: keyword(SwiftSyntax.Keyword.func)
├─name: identifier("test")
├─genericParameterClause: GenericParameterClauseSyntax
│ ├─leftAngle: leftAngle
│ ├─parameters: GenericParameterListSyntax
│ │ ├─[0]: GenericParameterSyntax
│ │ │ ├─attributes: AttributeListSyntax
│ │ │ ├─name: identifier("Root")
│ │ │ ╰─trailingComma: comma
│ │ ╰─[1]: GenericParameterSyntax
│ │   ├─attributes: AttributeListSyntax
│ │   ├─name: identifier("Value")
│ │   ├─colon: colon
│ │   ╰─inheritedType: IdentifierTypeSyntax
│ │     ╰─name: identifier("Comparable")
│ ╰─rightAngle: rightAngle
├─signature: FunctionSignatureSyntax
│ ├─parameterClause: FunctionParameterClauseSyntax
│ │ ├─leftParen: leftParen
│ │ ├─parameters: FunctionParameterListSyntax
│ │ │ ├─[0]: FunctionParameterSyntax
│ │ │ │ ├─attributes: AttributeListSyntax
│ │ │ │ ├─modifiers: DeclModifierListSyntax
│ │ │ │ ├─firstName: identifier("dump")
│ │ │ │ ├─secondName: identifier("a")
│ │ │ │ ├─colon: colon
│ │ │ │ ├─type: IdentifierTypeSyntax
│ │ │ │ │ ├─name: identifier("KeyPath")
│ │ │ │ │ ╰─genericArgumentClause: GenericArgumentClauseSyntax
│ │ │ │ │   ├─leftAngle: leftAngle
│ │ │ │ │   ├─arguments: GenericArgumentListSyntax
│ │ │ │ │   │ ├─[0]: GenericArgumentSyntax
│ │ │ │ │   │ │ ├─argument: IdentifierTypeSyntax
│ │ │ │ │   │ │ │ ╰─name: identifier("Root")
│ │ │ │ │   │ │ ╰─trailingComma: comma
│ │ │ │ │   │ ╰─[1]: GenericArgumentSyntax
│ │ │ │ │   │   ╰─argument: IdentifierTypeSyntax
│ │ │ │ │   │     ╰─name: identifier("Value")
│ │ │ │ │   ╰─rightAngle: rightAngle
│ │ │ │ ╰─trailingComma: comma
│ │ │ ├─[1]: FunctionParameterSyntax
│ │ │ │ ├─attributes: AttributeListSyntax
│ │ │ │ ├─modifiers: DeclModifierListSyntax
│ │ │ │ ├─firstName: identifier("b")
│ │ │ │ ├─colon: colon
│ │ │ │ ├─type: IdentifierTypeSyntax
│ │ │ │ │ ╰─name: identifier("String")
│ │ │ │ ╰─trailingComma: comma
│ │ │ ╰─[2]: FunctionParameterSyntax
│ │ │   ├─attributes: AttributeListSyntax
│ │ │   ├─modifiers: DeclModifierListSyntax
│ │ │   ├─firstName: identifier("c")
│ │ │   ├─colon: colon
│ │ │   ╰─type: IdentifierTypeSyntax
│ │ │     ╰─name: identifier("Int")
│ │ ╰─rightParen: rightParen
│ ├─effectSpecifiers: FunctionEffectSpecifiersSyntax
│ │ ╰─throwsClause: ThrowsClauseSyntax
│ │   ╰─throwsSpecifier: keyword(SwiftSyntax.Keyword.throws)
│ ╰─returnClause: ReturnClauseSyntax
│   ├─arrow: arrow
│   ╰─type: IdentifierTypeSyntax
│     ╰─name: identifier("Bool")
╰─body: CodeBlockSyntax├─leftBrace: leftBrace├─statements: CodeBlockItemListSyntax│ ╰─[0]: CodeBlockItemSyntax│   ╰─item: PrefixOperatorExprSyntax│     ├─operator: prefixOperator("!")│     ╰─expression: MemberAccessExprSyntax│       ├─base: DeclReferenceExprSyntax│       │ ╰─baseName: identifier("b")│       ├─period: period│       ╰─declName: DeclReferenceExprSyntax│         ╰─baseName: identifier("isEmpty")╰─rightBrace: rightBrace

代码看起来很长,似乎有点儿“一望无际”。不过如果我们查看它的 description 属性就会恍然大悟:它其实就是 @nilable 宏修饰方法所对应的语法树。

在这里插入图片描述

所以基本上来说,我们只需要将 declaration 的内容“修剪”为想要的结果即可。对于我们这个例子,就是将 declaration 中第一个 KeyPath 参数中的 Value 变为可选类型 Value?。

下面是我们第一种实现,因为考虑到了“调皮的”用户可能犯的各种错误,所以比较冗长。不过别急,后面我们会给出精简后的方案:

public static func expansion(of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws -> [DeclSyntax] {guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {throw MacroExpansionErrorMessage("必须在方法上使用我哦!")}let funcName = funcDecl.namelet funcModifiers = funcDecl.modifierslet funcGenericParameterClause = funcDecl.genericParameterClauselet funcEffecSpecifier = funcDecl.signature.effectSpecifiers?.description ?? ""let funcReturnClause = funcDecl.signature.returnClause?.description ?? ""let funcBody = funcDecl.bodyguard let funcFirstParamenter = funcDecl.signature.parameterClause.parameters.first else {throw MacroExpansionErrorMessage("方法必须至少要有 1 个参数哦!")}guard let funcFirstParamenterType = funcFirstParamenter.type.as(IdentifierTypeSyntax.self) else {throw MacroExpansionErrorMessage("方法参数格式错误哦!")}guard funcFirstParamenterType.name.description == "KeyPath" else {throw MacroExpansionErrorMessage("方法的第一个参数必须是 KeyPath 类型哦!")}guard let secondArgment = funcFirstParamenterType.genericArgumentClause?.arguments.last, secondArgment.argument.as(OptionalTypeSyntax.self) == nil else {throw MacroExpansionErrorMessage("方法 KeyPath 参数中的 Value 不是 Optional 类型哦!")}let firstName = funcFirstParamenter.firstName.descriptionvar firstArg = if let secondName = funcFirstParamenter.secondName?.description {"\(firstName)\(secondName): "} else {"\(firstName): "}let rootName = funcFirstParamenterType.genericArgumentClause!.arguments.first!.argument.as(IdentifierTypeSyntax.self)!.name.descriptionlet valueName = secondArgment.argument.as(IdentifierTypeSyntax.self)!.descriptionfirstArg += "KeyPath<\(rootName), \(valueName)?>"let parameters = funcDecl.signature.parameterClause.parametersvar otherParametersDesc = ""if parameters.count > 1 {for parameter in parameters.dropFirst() {otherParametersDesc += "\(parameter.description)"}}let nilFuncDecl = try FunctionDeclSyntax("\(funcModifiers)func \(funcName)\(funcGenericParameterClause)(\(raw: firstArg), \(raw: otherParametersDesc)) \(raw: funcEffecSpecifier)\(raw: funcReturnClause) \(funcBody)")return [.init(nilFuncDecl)]
}

在上面的代码中,我们做了这样几件事:

  • 为用户在构造宏所修饰的表达式时可能犯的各种错误(比如用户在属性而不是方法上应用 @nilable 宏)提供贴心的提示;
  • 确定 KeyPath 中 Value 的名称;
  • 考虑到 KeyPath 参数可能出现的名称前缀;
  • 确定方法签名中的其它信息(比如其它参数类型和返回类型);
  • 考虑到方法本身可能存在一些 modifier 和 effectSpecifiers 修饰器(比如 static、private、throws 等);

5.5 验证宏展开后的结果

回到 main.swift 源代码文件中,在选中 @nilable 宏关键字后将其展开,如果不出意外我们应该可以正确创建 sortItemsBy 排序方法的 KeyPath 可选 Value 版本(Value?):

在这里插入图片描述

这意味着,下面这些代码都可以顺利编译通过了:

let itemsByName = try! model.sortItemsBy(keyPath: \.name)
let itmesByNickname = try! model.sortItemsBy(keyPath: \.nickname)

棒棒哒!成就感爆棚的小伙伴们赶快给自已一个大大的赞吧!👍🏻


其实验证 Swift 宏展开结果最好的方法是使用单元测试,但由于目前宏单元测试略显“呆滞和笨拙”,所以暂时略去不表。


6. 另一个简洁的解决方案

虽然我们解决了问题,但上述实现实在是有点“臃肿不堪”。

让我们马上再做一次头脑风暴:我们实际只是想修改 sortItemsBy() 方法 KeyPath 形参中的 Value,如果只考虑几种可能出现的错误,仅需一个简单的字符串内容替换操作即可大功告成!

下面是我们 expansion() 方法简化后的新实现:

public static func expansion(of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws -> [DeclSyntax] {guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {throw MacroExpansionErrorMessage("必须在方法上使用我哦!")}var funcDesc = funcDecl.descriptionfuncDesc.replace(/@nilable/, with: "")let valueFinder = /.*KeyPath<.+?,\s*(.+)>,/guard let value = try valueFinder.firstMatch(in: funcDesc)?.output.1 else {throw MacroExpansionErrorMessage("找不到 KeyPath !")}funcDesc.replaceSubrange(value.startIndex..<value.endIndex, with: "\(value)?")    return [.init(try FunctionDeclSyntax("\(raw: funcDesc)"))]
}

在上面的代码中,我们只是仅仅替换原方法里形参 KeyPath 中的值类型 Value 为 Value? 而已,真是简单的不要不要的。

验证 @nilable 宏展开分结果,和之前是一毛一样的,棒棒哒!💯

7. 目前 Swift 宏的不足之处

虽然 Swift Macros 更好地弥补了 Swift 语言动态性不足的“先天缺陷”,让秃头码农们更优雅的遵循 “KISS” 原则,避免了代码重复。

但是,目前 Swift 宏仍有一些白圭之玷,它们主要表现在如下几个方面:

  • 无论我们是否喜欢,即使一个非常简单的宏实现都需要放在 Swift Package 中;
  • 官方示例很少,研究起来比较容易掉头发;
  • 定义 7 种宏的“隐藏”选项几乎没有文档,更容易掉头发;
  • FunctionDeclSyntax 等一些声明语法实体都是只读的,不太灵活;
  • 虽然像 FunctionDeclSyntax 这些声明语法实体都包含 ResultBuilder 构造器,但实际无法像 SwiftUI 那样自由组合内部元素;
  • 因为要实现庞大 Swift 语言的类型安全,所以 Swift 宏背后语法树的结构也很复杂,经常云里雾里的感觉;
  • 貌似宏的实现有缓存,所以在 Xcode 16 中修改宏的展开代码有时不能及时触发测试用例中宏展开结果的刷新,需要重启 Xcode;

以上只是其中一部分“美中不足”,其它一些小问题并没有完全列出来。

其实,现在 Swift 宏最大的问题就是它缺乏文档而且太复杂了,这就是为什么目前没什么人用的原因。

希望苹果在 WWDC 25 的 Swift Macros 2.0 中(如果可能的话)可以简化和改善它。

即便如此,雪中送炭的 Swift 宏仍然给了我们太多惊喜和便利,值得小伙伴们进一步深入挖掘。

本系列文章至此告一段落了,有机会我们会单写几篇 Swift 宏的进阶博文,敬请期待吧!

总结

在本篇博文中,我们介绍了宏展开方法中 declaration 参数的构成(FunctionDeclSyntax ),并详细讨论了自定义宏主体的实现;我们随后还精简了宏的展开逻辑并顺便聊了聊当前 Swift 宏的一些不足之处。

感谢观赏,再会啦!😎

相关文章:

Swift 宏(Macro)入门趣谈(五)

概述 苹果在去年 WWDC 23 中就为 Swift 语言新增了“其利断金”的重要小伙伴 Swift 宏&#xff08;Swift Macro&#xff09;。为此&#xff0c;苹果特地用 2 段视频&#xff08;入门和进阶&#xff09;颇为隆重的介绍了它。 那么到底 Swift 宏是什么&#xff1f;有什么用&…...

Linux 35.6 + JetPack v5.1.4@DeepStream安装

Linux 35.6 JetPack v5.1.4DeepStream安装 1. 源由2. 步骤Step 1 安装Jetpack 5.1.4 L4T 35.6Step 2 安装依赖组件Step 3 安装librdkafkaStep 4 安装 DeepStream SDKStep 5 测试 deepstream-appStep 6 运行 deepstream-app 3. 总结3.1 版本问题3.2 二进制help 4. 参考资料 1. …...

C++基础:list的底层实现

文章目录 1.基本结构2.迭代器的实现2.1 尾插的实现2.2 迭代器的实现 3.打印函数(模版复用实例化)4.任意位置的插入删除1. 插入2. 删除 5.析构与拷贝构造5.1 析构函数5.2 拷贝构造5.3 赋值重载 1.基本结构 与vector和string不同list需要: 一个类来放入数据和指针也就是节点 一…...

Spring中@Transactional注解与事务传播机制

文章目录 事务传播机制事务失效的场景 事务传播机制 事务的传播特性指的是 当一个事务方法调用另一个事务方法时&#xff0c;事务方法应该如何执行。 事务传播行为类型外部不存在事务外部存在事务使用方式REQUIRED(默认)开启新的事务融合到外部事务中Transactional(propagati…...

实验七 用 MATLAB 设计 FIR 数字滤波器

实验目的 加深对窗函数法设计 FIR 数字滤波器的基本原理的理解。 学习用 Matlab 语言的窗函数法编写设计 FIR 数字滤波器的程序。 了解 Matlab 语言有关窗函数法设计 FIR 数字滤波器的常用函数用法。 掌握 FIR 滤波器的快速卷积实现原理。 不同滤波器的设计方法具有不同的优…...

Linux - selinux

七、selinux 1、说明 SELinux是Security-Enhanced Linux的缩写&#xff0c;意思是安全强化的linux。 SELinux是对程序、文件等权限设置依据的一个内核模块。由于启动网络服务的也是程序&#xff0c;因此刚好也 是能够控制网络服务能否访问系统资源的一道关卡。 传统的文件权…...

【STL】C++ vector类模板

文章目录 基本概念vector的使用定义和初始化构造函数赋值操作容量和大小插入和删除数据存取 互换容器vector的迭代器vector储存自定义数据类型 基本概念 vector是类型相同的对象的容器&#xff0c;vector的大小可以变化&#xff0c;可以向数组中增加元素。因此&#xff0c;vec…...

物联网——WatchDog(监听器)

看门狗简介 独立看门狗框图 看门狗原理&#xff1a;定时器溢出&#xff0c;产生系统复位信号&#xff1b;若定时‘喂狗’则不产生系统复位信号 定时中断基本结构&#xff08;对比&#xff09; IWDG键寄存器 独立看门狗超时时间 WWDG(窗口看门狗) WWDG特性 WWDG超时时间 由于…...

从零开始写游戏之斗地主-网络通信

在确定了数据结构后&#xff0c;原本是打算直接开始写斗地主的游戏运行逻辑的。但是突然想到我本地写出来之后&#xff0c;也测试不了啊&#xff0c;所以还是先写通信模块了。 基本框架 在Java语言中搞网络通信&#xff0c;那么就得请出Netty这个老演员了。 主要分为两个端&…...

【智能控制】实验,基于MATLAB的模糊推理系统设计,模糊控制系统设计

关注作者了解更多 我的其他CSDN专栏 过程控制系统 工程测试技术 虚拟仪器技术 可编程控制器 工业现场总线 数字图像处理 智能控制 传感器技术 嵌入式系统 复变函数与积分变换 单片机原理 线性代数 大学物理 热工与工程流体力学 数字信号处理 光电融合集成电路…...

Vega Editor 基于 Web 的图形编辑器

Vega Editor 是一个强大的基于 Web 的图形编辑器&#xff0c;专为 Vega 和 Vega-Lite 可视化语法设计。它提供了一个交互式的环境&#xff0c;用户可以在其中编写、预览和分享他们的 Vega 和 Vega-Lite 可视化作品。Vega 和 Vega-Lite 是用于声明性可视化的开源语法&#xff0c…...

SQL 中SET @variable的使用

在 SQL 中&#xff0c;SET variable 用于声明和赋值用户定义的变量。具体来说&#xff0c; 符号用于表示一个局部变量&#xff0c;可以在 SQL 语句中存储和使用。它通常在存储过程、函数或简单的 SQL 查询中使用。 1. 声明并赋值给变量 你可以使用 SET 语句给一个变量赋值。例…...

基于 Vite 封装工具库实践

项目背景&#xff1a;公司在多个项目中频繁使用相同的工具函数。为了避免每次开发新项目时都重复复制代码&#xff0c;决定通过 Vite 封装一个时间函数组件库。该库将被发布到 Verdaccio 供团队其他项目使用。 项目介绍 本项目封装了一个时间函数工具库&#xff0c;使用 Momen…...

Oracle DataGuard 主备正常切换 (Switchover)

前言 众所周知&#xff0c;DataGuard 的切换分为两种情况&#xff1a; 系统正常情况下的切换&#xff1a;这种方式称为 switchover&#xff0c;是无损切换&#xff0c;不会丢失数据。灾难情况下的切换&#xff1a;这种情况下一般主库已经启动不起来了&#xff0c;称为 failov…...

[Redis#13] cpp-redis接口 | set | hash |zset

目录 Set 1. Sadd 和 Smembers 2. Sismember 3. Scard 4. Spop 5. Sinter 6. Sinter store Hash 1. Hset 和 Hget 2. Hexists 3. Hdel 4. Hkeys 和 Hvals 5. Hmget 和 Hmset Zset 1. Zadd 和 Zrange 2. Zcard 3. Zrem 4. Zscore cpp-redis 的学习 主要关注于…...

青海摇摇了3天,技术退步明显.......

最近快手上的青海摇招聘活动非常火热&#xff0c;我已经在思考是否备战张诗尧的秋招活动。开个玩笑正片开始&#xff1a; 先说一下自己的情况&#xff0c;大专生&#xff0c;20年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c…...

Flask+Minio实现断点续传技术教程

什么是MinIO MinIO是一个高性能的分布式对象存储服务&#xff0c;与Amazon S3 API兼容。它允许用户存储和检索任意规模的数据&#xff0c;非常适合于使用S3 API的应用程序。MinIO支持多租户存储&#xff0c;提供高可用性、高扩展性、强一致性和数据持久性。它还可以作为软件定义…...

Java中Logger定义的三种方式

在 Java 项目中&#xff0c;日志记录是开发中的一个重要部分&#xff0c;用于跟踪系统运行状态、排查问题以及记录重要事件。在定义日志记录器时&#xff0c;经常会遇到一些写法上的选择&#xff0c;比如 Logger 的作用域、是否使用静态变量&#xff0c;以及如何命名变量。本篇…...

模型压缩技术

目录 模型压缩技术 权重剪枝: 量化技术: 知识蒸馏: 低秩分解: 一、权重剪枝 二、量化技术 三、知识蒸馏 四、低秩分解 模型压缩技术 权重剪枝: 描述:通过删除模型中不重要的权重来减少参数数量和计算量。举例说明:假设我们有一个神经网络模型,其中某些神经元的…...

面试题整理

1 spring使用中有哪些设计模式 工厂模式-beanFactory,代理模式-aop,单例模式-每个bean默认都是单例的,原型模式-当将bean的作用域改为prototype时每次获取bean时使用了原型模式创建对象,责任链模式-dispatchServle查找url对应的处理器映射器时使用了,观察者模式-spring的…...

Linux

1、显示系统中所有进程 ps -ef运行效果&#xff1a; [rootredhat-9 ~]# ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 19:01 ? 00:00:01 /usr/lib/systemd/systemd rhgb --switched-r root 2 0 0…...

力扣_2389. 和有限的最长子序列

力扣_2389. 和有限的最长子序列 给你一个长度为 n 的整数数组 nums &#xff0c;和一个长度为 m 的整数数组 queries 。 返回一个长度为 m 的数组 answer &#xff0c;其中 answer[i] 是 nums 中 元素之和小于等于 queries[i] 的 子序列 的 最大 长度 。 子序列 是由一个数组…...

UI设计从入门到进阶,全能实战课

课程内容&#xff1a; ├── 【宣导片】从入门到进阶!你的第一门UI必修课!.mp4 ├── 第0课&#xff1a;UI知识体系梳理 学习路径.mp4 ├── 第1课&#xff1a;IOS设计规范——基础规范与切图.mp4 ├── 第2课&#xff1a;IOS新趋势解析——模块规范与设计原则(上).mp4…...

Formality:等价性检查的流程与模式(Guide、Setup、Preverify、Match与Verify)

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 等价性检查的流程 图1概述了使用Formality进行等效性检查的具体步骤。 图1 等价性检查流程 启动Formality(Start Formality) 要启动Formality&#xff0c;请…...

【Linux】————(日志、线程池及死锁问题)

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux 创作时间 &#xff1a;2024年11月29日 日志 关于日志&#xff0c;首先我们来说一下日志的作用&#xff0c; 作用&#xff1a; 问题追踪&#xff1a;通过日志不仅仅包括我们程序的一些bug&#xff0c;也可以在…...

【自动化】配置信息抽取

公共基本信息配置文件抽取 公共基本信息比如卖家、买家、管理员&#xff0c;验证码等基本信息&#xff0c;再比如数据库、redis、各个服务的域名&#xff0c;这些目前是写死在代码之中的&#xff0c;为了能够更好的维护他们&#xff0c;我们将他们放入配置文件进行管理 公共的…...

Python毕业设计选题:基于django+vue的校园影院售票系统

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 影院信息管理 电影类型管理 电影信息管理 系统…...

Docker化部署Flask:轻量级Web应用的快速部署方案

Flask是一个用Python编写的轻量级Web应用框架&#xff0c;以其简洁性和灵活性而受到开发者的喜爱。Docker作为一种流行的容器化技术&#xff0c;为应用的部署和管理提供了极大的便利。本文将探讨Flask的优点、Docker部署的好处&#xff0c;并详细介绍如何将Flask应用Docker化部…...

centos怎么通过docker安装一个gitlab

在CentOS上通过Docker安装GitLab的步骤如下&#xff1a; 安装Docker引擎&#xff1a; 首先&#xff0c;需要在你的CentOS系统上安装Docker。可以通过以下命令来安装Docker&#xff1a;yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/lin…...

docker 运行my-redis命令

CREATE TABLE orders ( order_id bigint NOT NULL COMMENT "订单ID", dt date NOT NULL COMMENT "日期", merchant_id int NOT NULL COMMENT "商家ID", user_id int NOT NULL COMMENT "用户ID", good_id int NOT NULL COMMENT "商…...

qt6.4.0+visual studio2022+opencv

qt6.4.0visual studio2022opencv 补充&#xff1a;在安装完Qt后还需要配置环境变量...

23种设计模式之适配器模式

目录 1. 简介1.1 定义1.2 结构和组成部分 2. 代码2.1 MediaPlayer2.2 AdvanceMediaPlayer2.3 VicPlayer2.4 Mp4Player2.5 MediaPlayerAdapter2.6 AudioPlayer2.7 Test 3. 适用场景4. 优点和缺点5. 总结 1. 简介 1.1 定义 适配器模式&#xff08;Adapter Pattern&#xff09;是…...

剖析go协程池实现原理

go协程池实现 在go语言编程中有一种池肯定避免不了&#xff0c;那就是-协程池&#xff0c;无论你是日常工作还是面试中面试官都无法避免协程池&#xff0c;掌握协程池你也就算是入门go的并发编程了&#xff0c;打一波广告后面会有专门的文章来介绍如何在go中进行并发编程。 协…...

渗透测试--Linux上获取凭证

在测试过程中我们也会发现一些Linux主机加域的情况&#xff0c;虽然不多见&#xff0c;但它确实存在。正所谓技多不压身&#xff0c;这样能够触类旁通的知识&#xff0c;我们怎能错过&#xff0c;所以在此我们将会主要探讨从Linux主机上获取域凭证的方法。主要有以下内容&#…...

【笔记】自动驾驶预测与决策规划_Part9_数据驱动前沿算法与发展趋势

文章目录 数据驱动前沿算法与发展趋势0. 前言1. 端到端自动驾驶引言2. 端到端自动驾驶2.1 端到端自动驾驶早期尝试 ALVINN2.2 基于模仿学习的端到端系统 NVIDIA-E2E2.3 基于强化学习的端到端系统2.4 多模态融合的自动驾驶 Transfuser2.5 模块化端到端 UniAD2.6 模块化端到端 VA…...

工业公辅车间数智化节能头部企业,蘑菇物联选择 TDengine 升级 AI 云智控

小T导读&#xff1a;在工业节能和智能化转型的浪潮中&#xff0c;蘑菇物联凭借其自研的灵知 AI 大模型走在行业前沿&#xff0c;为高能耗设备和公辅能源车间提供先进的 AI 解决方案。此次采访聚焦于蘑菇物联与 TDengine 的合作项目&#xff0c;通过 AI 云智控平台的建设&#x…...

【Linux】开启你的Linux之旅:初学者指令指南

Linux相关知识点可以通过点击以下链接进行学习一起加油&#xff01; 在 Linux 开发中&#xff0c;GDB 调试器和 Git 版本控制工具是开发者必备的利器。GDB 帮助快速定位代码问题&#xff0c;Git 则提供高效的版本管理与协作支持。本指南将简明介绍两者的核心功能与使用技巧&…...

Vite 6.0 发布:引领现代前端开发新方向

Vite 6.0 带来了大量更新与优化&#xff0c;旨在简化开发流程、提升性能&#xff0c;并解决现代 Web 开发中的诸多挑战。本次更新引入了 实验性环境 API 和现代化的工具链&#xff0c;进一步巩固了 Vite 作为开发者首选工具的地位。以下是关于新特性、生态发展以及重要更新的全…...

深入了解阿里云 OSS:强大的云存储解决方案

在现代互联网应用中&#xff0c;数据存储是一个不可忽视的环节。随着数据量的不断增长&#xff0c;传统的存储方式已经无法满足高速、低成本、大容量的需求。阿里云 OSS&#xff08;对象存储服务&#xff09;作为一种高性能、低成本且具备高度扩展性的云存储服务&#xff0c;已…...

canvas绘制网络地址图片

canvas在绘制网络地址图片时&#xff0c;需要先下载成临时路径 export function downLoadBgImg (url) {return new Promise((r,j) > {uni.downloadFile({url,success : res > {if (res.statusCode 200) {r(res.tempFilePath);return;};j(依赖文件下载失败);},fail : er…...

《DSL-FIQA》论文翻译

《DSL-FIQA: Assessing Facial Image Quality Via Dual-Set Degradation Learning and Landmark-Guided Transformer》 原文链接&#xff1a;DSL-FIQA: Assessing Facial Image Quality via Dual-Set Degradation Learning and Landmark-Guided Transformer | IEEE Conference…...

【Linux网络编程】第四弹---构建UDP服务器与字典翻译系统:源码结构与关键组件解析

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、UdpServer.hpp 1.1、函数对象声明 1.2、Server类基本结构 1.3、构造函数 1.4、Start() 2、Dict.hpp…...

【人工智能】人工智能,深度学习与人工神经网络

人工智能 人工智能一、定义与核心要素二、主要方法与技术三、应用领域四、发展前景与挑战五、分类六、研究目标与价值 深度学习定义与核心思想网络结构工作原理关键技术与模型应用领域发展与挑战 人工神经网络一、定义与原理二、基本特性三、网络结构四、工作原理五、应用领域六…...

嵌入式系统应用-LVGL的应用-平衡球游戏 part2

平衡球游戏 part2 4 mpu60504.1 mpu6050 介绍4.2 电路图4.3 驱动代码编写 5 游戏界面移植5.1 移植源文件5.2 添加头文件 6 参数移植6.1 4 mpu6050 4.1 mpu6050 介绍 MPU6050是一款由InvenSense公司生产的加速度计和陀螺仪传感器&#xff0c;广泛应用于消费电子、机器人等领域…...

Linux网络编程之---多线程实现并发服务器

下面我们来使用tcp集合多线程实现并发服务器 一.服务端 #include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <pthread.h>typedef struct sockinfo {char ip[16];unsigne…...

架构师的英文:Architect

中文版 软件架构师 的英文是 “Software Architect”。 Software: 软件Architect: 架构师&#xff0c;通常指的是设计和规划某种系统或结构的人。 Software Architect 通常负责软件系统的整体设计、技术选型、架构规划&#xff0c;确保系统的可扩展性、可维护性和高效性等。…...

量化交易系统开发-实时行情自动化交易-8.7.文华平台

19年创业做过一年的量化交易但没有成功&#xff0c;作为交易系统的开发人员积累了一些经验&#xff0c;最近想重新研究交易系统&#xff0c;一边整理一边写出来一些思考供大家参考&#xff0c;也希望跟做量化的朋友有更多的交流和合作。 接下来会对于文华平台介绍。 文华财经…...

【前端】JavaScript 中的创建对象模式要点

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;对象属性值中的引号规则&#x1f4af;对象属性换行与尾随逗号的使用&#x1f4af;工厂模式&#xff1a;灵活高效的对象创建&#x1f4af;自定义构造函数&#xff1a;通过…...

鸿蒙NEXT元服务:论如何免费快速上架作品

【引言】天下武功&#xff0c;唯快不破。 本文讨论如何免费且以最快速度上架自己的作品。 作者以自己从零开始到提交发布审核一共俩小时的操作流程分享给大家作参考。 【1】立项选择 结论&#xff1a;元服务&#xff0c;单机&#xff0c;工具类&#xff08;非游戏&#xff…...

hive3.1.3安装及基本例子

前提要安装好hadoop环境和mysql。 1、下载并解压 https://archive.apache.org/dist/hive/hive-3.1.3/apache-hive-3.1.3-bin.tar.gz 下载bin包到/app/src中。 cd /app/src/ tar zxvf apache-hive-3.1.3-bin.tar.gz mv apache-hive-3.1.3-bin /app/hive2、配置path nano /etc…...