近期的一个项目需要用到SM3加密算法,需要在iOS中使用Objective-C实现SM3国密加密算法。

SM3:是中国国家密码管理局发布的密码杂凑算法标准,适用于商用密码应用中的数字签名和验证、消息认证码的生成与验证以及随机数的生成等

由于iOS系统并未内置SM3算法,我们需要使用第三方开源库或自己实现

GMObjC库: 是一个基于 OpenSSL 的国密(SM2、SM3、SM4)算法的 Objective-C 开源库,适用于 iOS 和 macOS 开发。它封装了中国国家密码管理局发布的多种加密算法,包括:

1. SM2: 支持基于椭圆曲线(ECC)的加解密,密钥协商(ECDH)和签名算法

2. SM3: 类似 SHA 系列的国密哈希算法,包含 SM3 和 HMAC 等

3. SM4: 实现对称分组加密算法

**GmSSL库:**GmSSL是由北京大学自主开发的国产商用密码开源库,实现了对国密算法、标准和安全通信协议的全面功能覆盖,支持包括移动端在内的主流操作系统和处理器,支持密码钥匙、密码卡等典型国产密码硬件,提供功能丰富的命令行工具及多种编译语言编程接口

方案一:使用第三方库(GMObjC)

集成GMObjC:集成GMObjC方法

因为我们的项目是SDK不便用CocoaPods方法,因此我只能选择直接集成和手动编译为 Framework。

1.直接集成 (demo)

1.从 Git 下载最新代码,找到和 README 同级的 GMObjC 文件夹,将 GMObjC 文件夹拖入项目

2.找到和 README 同级的 Frameworks 文件夹,将项目 Frameworks/OpenSSL.xcframework 拖入项目

3.在需要使用的地方导入头文件 GMObjC.h 即可使用 SM2、SM4 加解密,签名验签,计算 SM3 摘要等

注意事项

GMObjC 依赖 OpenSSL,可直接拖入 Frameworks/OpenSSL.xcframework 或通过pod GMOpenSSL安装 OpenSSL。

如果项目中已集成 OpenSSL 1.1.1l 以上版本,可共用同一个 OpenSSL;否则需要使用 Carthage 将 GMObjC 编译为动态库。

我按照以上步骤将文件导入后报错:
OpenSSL.xcframework 签名验证失败

OpenSSL.xcframework报错

终端执行强制重签名命令

codesign --force --deep --sign - 你的路径/OpenSSL.xcframework

返回:你的路径/OpenSSL.xcframework: replacing existing signature

现在就可以运行测试了:

#import "GMObjC.h"NSString *str = @"123@1234";
NSString *digest = [GMSm3Utils hashWithText:str];
NSLog(@"%@", digest);

2.手动编译为 Framework (demo)

1.动态库‌:

从 GitHub 下载源码,打开项目GMObjC-master/Frameworks/GMObjC.xcframework把这个拖入项目

在 Xcode 的 General → Frameworks, Libraries, and Embedded Content 中需标记为 Embed & Sign

Embed & Sign

#import "GMObjC/GMObjC.h"NSString *digest1 = [GMSm3Utils hashWithText:str];
NSLog(@"%@", digest1);

2.静态库:

从 GitHub 下载源码,打开项目 GMObjC.xcodeproj,设置 Build Settings - Linking-General - Mach-O Type 为 Static Library

手动编译为静态库 GMObjC.framework

**合并为 XCFramework:**通过xcodebuild -create-xcframework命令来合并为 XCFramework,通过合并 GMObjC 库的模拟器和真机版本来演示

# 创建合并包 GMObjC.xcframeworkxcodebuild -create-xcframework \-framework Release-iphoneos/GMObjC.framework \-framework Release-iphonesimulator/GMObjC.framework \-output GMObjC.xcframework

把生成的GMObjC.xcframework拖入项目即可

3.CocoaPods安装GMObjC (GMObjC-demo) (GMDynamic-demo)

GMObjC 和 GMDynamic 只能安装其中一个,二者不能同时安装。

GMObjC 为静态库,GMDynamic 为编译好的 GMObjC 动态库版本。

# 安装 GMObjC 的源码和 GMOpenSSL.xcframework (静态库)
pod 'GMObjC', '~> 4.0.3'# 当 Podfile 中使用 use_frameworks! 时,安装 GMObjC.xcframework (动态库)
pod 'GMDynamic', '~> 4.0.3'

方案二:使用第三方库(GmSSL)(demo)

集成GmSSL:

集成GmSSL方法

但是我用这种方法不行,我用了其他的方法。

我们使用GmSSL 3.x(master分支)来编译iOS的静态库(libcrypto.a和libssl.a)。由于3.x版本采用了CMake构建系统,因此流程与2.x不同。

GmSSL 3.x 的构建系统已经发生了变化,生成的库文件名为 libgmssl.a 而不是传统的 libcrypto.a 和 libssl.a。

如果项目必须使用 libcrypto.alibssl.a,请回退到 GmSSL 2.x

  1. 克隆代码并切换到master分支(或最新的稳定标签)

  2. 配置CMake工具链文件(为iOS交叉编译)

  3. 分别编译arm64(真机)和x86_64(模拟器)架构

  4. 使用lipo合并成通用静态库

  5. 将生成的静态库和头文件集成到iOS项目中。

创建编译脚本: build_ios.sh(放在GmSSL根目录)

#!/bin/bash
set -e# 确保使用正确的路径
export PATH="/usr/local/bin:$PATH"# 设置环境变量
export XCODE_PATH=$(xcode-select -p)
export IOS_SDK=$XCODE_PATH/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
export SIM_SDK=$XCODE_PATH/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk# 创建输出目录
OUTPUT_DIR="build-ios"
rm -rf $OUTPUT_DIR
mkdir -p $OUTPUT_DIR# 编译函数
compile_arch() {ARCH=$1SDK=$2BUILD_DIR="${OUTPUT_DIR}/${ARCH}"mkdir -p $BUILD_DIRpushd $BUILD_DIR > /dev/nullecho "▸ 配置 $ARCH..."cmake ../.. \-DCMAKE_SYSTEM_NAME=iOS \-DCMAKE_OSX_ARCHITECTURES=$ARCH \-DCMAKE_OSX_SYSROOT=$SDK \-DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \-DCMAKE_BUILD_TYPE=Release \-DBUILD_SHARED_LIBS=OFF \-DENABLE_SM2=ON \-DENABLE_SM3=ON \-DENABLE_SM4=ON \-DENABLE_SM9=ON \-G Ninjaecho "▸ 编译 $ARCH..."ninja# 关键修改:GmSSL 3.x 生成的库是 libgmssl.amkdir -p libcp bin/libgmssl.a lib/popd > /dev/null
}# 编译各架构
compile_arch "arm64" "$IOS_SDK"
compile_arch "x86_64" "$SIM_SDK"# 合并通用库
UNIVERSAL_DIR="${OUTPUT_DIR}/universal"
mkdir -p $UNIVERSAL_DIR/lib# 合并为单个库 (GmSSL 3.x 只生成一个库)
lipo -create \"${OUTPUT_DIR}/arm64/lib/libgmssl.a" \"${OUTPUT_DIR}/x86_64/lib/libgmssl.a" \-output "$UNIVERSAL_DIR/lib/libgmssl.a"# 复制头文件
if [ -d "${OUTPUT_DIR}/arm64/include" ]; thencp -R "${OUTPUT_DIR}/arm64/include" "$UNIVERSAL_DIR/"
elif [ -d "../../include" ]; thencp -R "../../include" "$UNIVERSAL_DIR/"
elseecho "⚠️ 警告: 找不到头文件目录"
fiecho "✅ 编译成功!"
echo "库文件位置: $UNIVERSAL_DIR/lib/libgmssl.a"
echo "头文件位置: $UNIVERSAL_DIR/include"# 验证文件
file "$UNIVERSAL_DIR/lib"/*.a
lipo -info "$UNIVERSAL_DIR/lib/libgmssl.a"

然后按照以下步骤进行执行:

# 安装构建工具
brew install cmake ninja pkg-config# 获取最新代码
git clone https://github.com/guanzhi/GmSSL.git
cd GmSSL
git checkout master  # 确保使用最新版本
git pull# 2. 执行编译
chmod +x build_ios.sh
./build_ios.sh
  1. 将GmSSL/build-ios/universal/lib/libgmssl.a 拖入项目

  2. 将GmSSL/include/gmssl 拖入项目

  3. import “sm3.h”

封装方法:

@interface GmSSLEncryptorSM3 : NSObject+ (NSString *)sm3HashWithString:(NSString *)input;
+ (NSData *)sm3HashWithData:(NSData *)data;@end@implementation GmSSLEncryptorSM3+ (instancetype)encryptor {return [[GmSSLEncryptorSM3 alloc] init];
}+ (NSData *)sm3HashWithData:(NSData *)data {// 初始化 SM3 上下文SM3_CTX ctx;sm3_init(&ctx);// 添加数据到哈希计算sm3_update(&ctx, data.bytes, data.length);// 准备存储结果的缓冲区 (SM3 输出为 32 字节)uint8_t dgst[SM3_DIGEST_SIZE];// 完成哈希计算sm3_finish(&ctx, dgst);// 转换为 NSDatareturn [NSData dataWithBytes:dgst length:SM3_DIGEST_SIZE];
}+ (NSString *)sm3HashWithString:(NSString *)input {NSData *inputData = [input dataUsingEncoding:NSUTF8StringEncoding];// 计算 SM3 哈希NSData *hashData = [GmSSLEncryptorSM3 sm3HashWithData:inputData];// 转换为十六进制字符串显示NSMutableString *hexString = [NSMutableString string];const uint8_t *bytes = (const uint8_t *)hashData.bytes;for (NSUInteger i = 0; i < hashData.length; i++) {[hexString appendFormat:@"%02x", bytes[i]];}return hexString;
}
@end

就可以在项目中使用了:

NSString *encryptor = [GmSSLEncryptorSM3 sm3HashWithString:str];
NSLog(@"%@", encryptor);

方案三:纯 Objective-C 实现(无依赖)(demo)

SM3本质上不是加密算法,它是是一种杂凑函数,是在[SHA-256]基础上改进实现的一种算法,它不是对数据进行加密然后再解密,而是生成一个256位的散列值,因此SM3适用于内容摘要,数字签名验证或密码验证等。

SM3算法的执行过程:

根据SM3标准文档(GM/T 0004-2012)

sm3流程.png

**消息扩展:**将16个32位字扩展为68个字(W)和64个字(W1),使用P1宏。
**压缩函数:**64轮迭代更新寄存器(A-H),每轮使用FF1/GG1等宏。
**常量:**压缩函数中的常量0x7A879D8A(TJ的固定值)。
**结果输出:**将最终状态寄存器转换为大端序字节流(256位)。

//
//  SM3Encryptor.m
//  testDemo
//
//  Created by wt on 2025/6/12.
//#import "SM3Encryptor.h"
#include <stdint.h>// SM3 上下文结构
typedef struct {uint32_t state[8];   // 8个32位寄存器(A-H)uint64_t totalLength; // 总消息长度(位)uint8_t buffer[64];  // 当前数据块缓存uint32_t bufferLength; // 当前缓冲区长度
} SM3Context;// 循环左移
static inline uint32_t ROTL(uint32_t x, uint8_t n) {return (x << n) | (x >> (32 - n));
}// 布尔函数 FF0(0≤j≤15)
static inline uint32_t FF0(uint32_t x, uint32_t y, uint32_t z) {return x ^ y ^ z;
}// 布尔函数 FF1(16≤j≤63)
static inline uint32_t FF1(uint32_t x, uint32_t y, uint32_t z) {return (x & y) | (x & z) | (y & z);
}// 布尔函数 GG0(0≤j≤15)
static inline uint32_t GG0(uint32_t x, uint32_t y, uint32_t z) {return x ^ y ^ z;
}// 布尔函数 GG1(16≤j≤63)
static inline uint32_t GG1(uint32_t x, uint32_t y, uint32_t z) {return (x & y) | ((~x) & z);
}// 置换函数 P0
static inline uint32_t P0(uint32_t x) {return x ^ ROTL(x, 9) ^ ROTL(x, 17);
}// 置换函数 P1
static inline uint32_t P1(uint32_t x) {return x ^ ROTL(x, 15) ^ ROTL(x, 23);
}// 初始化SM3上下文
void SM3Init(SM3Context *context) {// SM3标准初始值context->state[0] = 0x7380166F;context->state[1] = 0x4914B2B9;context->state[2] = 0x172442D7;context->state[3] = 0xDA8A0600;context->state[4] = 0xA96F30BC;context->state[5] = 0x163138AA;context->state[6] = 0xE38DEE4D;context->state[7] = 0xB0FB0E4E;context->totalLength = 0;context->bufferLength = 0;memset(context->buffer, 0, 64);
}// 处理单个64字节块(压缩函数核心)
void SM3Compress(SM3Context *context, const uint8_t block[64]) {// 1. 消息扩展:16字 → 68字(W) + 64字(W1)uint32_t W[68], W1[64];// 初始化前16字(大端序转换)for (int i = 0; i < 16; i++) {W[i] = (uint32_t)block[i*4] << 24 |(uint32_t)block[i*4+1] << 16 |(uint32_t)block[i*4+2] << 8 |(uint32_t)block[i*4+3];}// 计算W[16]-W[67]for (int j = 16; j < 68; j++) {uint32_t temp = W[j-16] ^ W[j-9] ^ ROTL(W[j-3], 15);W[j] = P1(temp) ^ ROTL(W[j-13], 7) ^ W[j-6];}// 计算W1[0]-W1[63]for (int j = 0; j < 64; j++) {W1[j] = W[j] ^ W[j+4];}// 2. 寄存器初始化(A-H)uint32_t A = context->state[0];uint32_t B = context->state[1];uint32_t C = context->state[2];uint32_t D = context->state[3];uint32_t E = context->state[4];uint32_t F = context->state[5];uint32_t G = context->state[6];uint32_t H = context->state[7];// 3. 64轮迭代(严格遵循标准)for (int j = 0; j < 64; j++) {uint32_t SS1, SS2, TT1, TT2;// 常量选择(关键修正)uint32_t TJ = (j < 16) ? 0x79CC4519 : 0x7A879D8A;// 计算SS1/SS2(修正了TJ参数)SS1 = ROTL(ROTL(A, 12) + E + ROTL(TJ, j % 32), 7);SS2 = SS1 ^ ROTL(A, 12);// 计算TT1/TT2(使用内联函数)if (j < 16) {TT1 = FF0(A, B, C) + D + SS2 + W1[j];TT2 = GG0(E, F, G) + H + SS1 + W[j];} else {TT1 = FF1(A, B, C) + D + SS2 + W1[j];TT2 = GG1(E, F, G) + H + SS1 + W[j];}// 更新寄存器(严格顺序)D = C;C = ROTL(B, 9);B = A;A = TT1;H = G;G = ROTL(F, 19);F = E;E = P0(TT2);}// 4. 更新最终状态(与初始IV异或)context->state[0] ^= A;context->state[1] ^= B;context->state[2] ^= C;context->state[3] ^= D;context->state[4] ^= E;context->state[5] ^= F;context->state[6] ^= G;context->state[7] ^= H;
}// 更新数据(可分多次调用)
void SM3Update(SM3Context *context, const uint8_t *data, size_t length) {context->totalLength += length * 8; // 更新总位数(字节转位)// 处理缓冲区中的剩余空间if (context->bufferLength > 0) {size_t copySize = MIN(64 - context->bufferLength, length);memcpy(context->buffer + context->bufferLength, data, copySize);context->bufferLength += copySize;data += copySize;length -= copySize;if (context->bufferLength == 64) {SM3Compress(context, context->buffer);context->bufferLength = 0;}}// 处理完整块while (length >= 64) {SM3Compress(context, data);data += 64;length -= 64;}// 缓存剩余数据if (length > 0) {memcpy(context->buffer, data, length);context->bufferLength = length;}
}// 完成哈希计算
void SM3Final(SM3Context *context, uint8_t output[32]) {// 计算填充长度(SM3标准:补位1 + k个0 + 64位长度)size_t totalBits = context->totalLength;size_t paddingBits = (context->bufferLength < 56) ?(56 - context->bufferLength) :(120 - context->bufferLength);// 构建填充数据uint8_t padding[128] = {0};padding[0] = 0x80; // 补位起始位(二进制10000000)// 添加填充SM3Update(context, padding, paddingBits);// 添加消息长度(大端序64位)uint64_t bitCount = CFSwapInt64HostToBig(totalBits);SM3Update(context, (uint8_t *)&bitCount, 8);// 确保最后一个块被处理if (context->bufferLength > 0) {memset(context->buffer + context->bufferLength, 0, 64 - context->bufferLength);SM3Compress(context, context->buffer);}// 输出最终哈希(256位,大端序)for (int i = 0; i < 8; i++) {output[i*4]     = (uint8_t)(context->state[i] >> 24);output[i*4 + 1] = (uint8_t)(context->state[i] >> 16);output[i*4 + 2] = (uint8_t)(context->state[i] >> 8);output[i*4 + 3] = (uint8_t)(context->state[i]);}
}// Objective-C 封装接口
@implementation SM3Encryptor+ (NSData *)hashWithData:(NSData *)inputData {SM3Context context;SM3Init(&context);// 处理输入数据SM3Update(&context, inputData.bytes, inputData.length);// 获取结果uint8_t output[32];SM3Final(&context, output);return [NSData dataWithBytes:output length:32];
}+ (NSString *)hexStringWithData:(NSData *)inputData {NSData *hashData = [self hashWithData:inputData];const uint8_t *bytes = (const uint8_t *)hashData.bytes;NSMutableString *hex = [NSMutableString string];for (NSUInteger i = 0; i < hashData.length; i++) {[hex appendFormat:@"%02X", bytes[i]];}return [hex copy];
}+ (NSString *)hexStringWithInput:(NSString *)inputStr {NSData *inputData = [inputStr dataUsingEncoding:NSUTF8StringEncoding];NSData *hashData = [self hashWithData:inputData];const uint8_t *bytes = (const uint8_t *)hashData.bytes;NSMutableString *hex = [NSMutableString string];for (NSUInteger i = 0; i < hashData.length; i++) {[hex appendFormat:@"%02X", bytes[i]];}return [hex copy];
}@end

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

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

相关文章

[逆向工程]什么是TEB 与 PEB(二十九)

[逆向工程]什么是TEB 与 PEB(二十九) 一、引言:为什么需要了解 TEB/PEB? 在 Windows 系统开发、调试或逆向工程中,TEB(Thread Environment Block) 和 PEB(Process Environment Block) 是理解程序执行机制的关键。它们如同进程与线程的“身份证”,存储了从内存布局到…

逆向分析贝壳网人机验证JS加密逻辑

引言 在数据爬取和自动化测试过程中&#xff0c;人机验证&#xff08;如滑块、点选、短信验证等&#xff09;是常见的反爬手段。贝壳网&#xff08;ke.com&#xff09;作为国内领先的房产平台&#xff0c;其人机验证机制较为复杂&#xff0c;涉及前端JS加密、动态Token、行为检…

Vue3 + Element Plus中el-table加载状态分析

在 Vue 3 中&#xff0c;当 onMounted 钩子被触发时&#xff0c;父组件的 DOM 已经挂载完成&#xff0c;但子组件&#xff08;如 el-table&#xff09;可能尚未完成其内部渲染。具体分析如下&#xff1a; 1. onMounted 的执行时机 父组件挂载完成&#xff1a;onMounted 表示当前…

OpenCV图像拼接技术详解:从特征匹配到全景合成

本文将详细介绍如何使用OpenCV实现两幅图像的自动拼接&#xff0c;涵盖特征提取、单应性矩阵计算和图像融合等关键技术。 一、图像拼接概述 图像拼接是将多张有重叠区域的图像合并成一幅全景图的技术&#xff0c;广泛应用于全景摄影、卫星图像处理、医学影像等领域。其核心技术…

如何通过 5 种方式向 Android 手机添加音乐

想把音乐添加到你的安卓手机&#xff0c;然后随时随地无需网络连接就能欣赏你喜爱的音乐吗&#xff1f;这不再是麻烦。现在&#xff0c;你可以按照本指南中的有效方法&#xff0c;将音乐添加到你的安卓手机上。让我们在安卓手机上聆听我们美妙的歌曲吧。 第 1 部分&#xff1a;…

VS Code 项目中的 .vscode 目录详解

VS Code 项目中的 .vscode 目录详解 .vscode 目录是 VS Code 项目的核心配置中心&#xff0c;它包含特定于当前项目的配置&#xff0c;这些配置覆盖全局设置&#xff0c;确保团队成员获得一致的开发环境体验。 .vscode 目录中的核心文件 文件名作用是否应纳入版本控制settin…

Ubuntu22.04安装opengauss并配置远程访问、JDBC连接

内容概括 最近在研究怎么在ubuntu服务器环境下使用opengauss&#xff0c;看了下官方下载地址没有适配ubuntu的安装包。仔细翻了下官方文档&#xff0c;发现安装指南里有提供一个deb包安装方案&#xff0c;有适配ubuntu&#xff0c;经过实践可行&#xff0c;于是记录下来给有需要…

国产智能体“双子星”:实在Agent vs Manus(核心架构与技术实现路径对比)

2025年&#xff0c;人工智能领域迎来重要转折点——大模型的光环逐渐消散&#xff0c;落地应用成为行业焦点。 正如业内人士所言&#xff1a;“2023年&#xff0c;大家普遍觉得要买一个大模型&#xff0c;但训练完了怎么用起来&#xff0c;大家一头雾水。” 在这一背景下&…

pgAdmin 4 连接 postgreSQL

环境如下&#xff1a; 宿主机为Windows 11postgreSQL安装在宿主机上的Linux虚机中&#xff0c;Hypervisor是VirtualBoxpgAdmin 4 已安装在宿主机上 本文讲述&#xff1a;如何通过宿主机上的pgAdmin 连接到虚拟机中的PG。 设置监听 默认的PG监听主机为localhost&#xff0c;…

HTTP 缓存策略:强缓存与协商缓存的深入解析

在HTTP缓存策略中&#xff0c;强缓存和协商缓存是两种常用的机制&#xff0c;用于减少数据传输和提高网页加载速度。它们通过在客户端和服务器之间建立缓存来避免不必要的网络请求&#xff0c;从而优化性能并提高用户体验。本文将详细介绍这两种缓存策略的原理、优势和适用场景…

Node.js 中的 Token 认证机制详解

文章目录 Node.js 中的 Token 认证机制详解1. Token 认证基础1.1 什么是 Token 认证&#xff1f;1.2 Token 认证流程 2. JWT (JSON Web Token) 实现2.1 安装依赖2.2 生成 Token2.3 验证 Token 中间件 3. 完整实现示例3.1 登录接口3.2 受保护的路由 4. Token 安全最佳实践5. Tok…

23 - HaLoAttention模块

论文《Scaling Local Self-Attention for Parameter Efficient Visual Backbones》 1、作用 HaloNet通过引入Haloing机制和高效的注意力实现&#xff0c;在图像识别任务中达到了最先进的准确性。这些模型通过局部自注意力机制&#xff0c;有效地捕获像素间的全局交互&#xf…

2025Mybatis最新教程(五)

第5章 ORM映射 5.1 MyBatis自动ORM失效 MyBatis只能自动维护库表”列名“与”属性名“相同时的对应关系,二者不同时,无法自动ORM。 自动ORM失效建表 create table t_managers(mgr_id int primary key auto_increment,mgr_name varchar(50),mgr_pwd varchar(50) ); 添加数据…

解决lombok注解失效问题

Lombok 注解失效是 Java 开发中的常见问题&#xff0c;通常由依赖配置、IDE 支持或构建工具设置引起。最近在拉取别人springboot3jdk21版本的项目时遇到了lombok注解失效&#xff0c;导致项目无法启动的问题&#xff0c;以下是我的解决方案&#xff1a; 首先检查idea 的lombok…

3分钟搭建LarkXR实时云渲染PaaS平台,实现各类3D/XR应用的一键推流

LarkXR是由Paraverse平行云自主研发的去中心化实时云渲染平台&#xff0c;以其卓越的性能和丰富完备的功能插件&#xff0c;引领3D/XR云化行业风向标。LarkXR适用于3D/XR开发者、设计师、终端用户等创新用户&#xff0c;可以在零硬件负担下&#xff0c;轻松实现超高清低时延的3…

vue3 watch监视详解

watch监视 一 &#xff1a;watch监视{ref}定义的基本类型结构 <template><div class"person"><h1>情况一:watch监视{ref}定义的基本类型结构</h1><h1>当前的和为{{ sum }}</h1><button click"changeSum">点我…

TensorFlow Serving学习笔记2: 模型服务

本文深入剖析 TensorFlow Serving 的核心架构与实现机制&#xff0c;结合源码分析揭示其如何实现高可用、动态更新的生产级模型服务。 一、TensorFlow Serving 核心架构 1.1 分层架构设计 TensorFlow Serving 采用模块化分层设计&#xff0c;各组件职责分明&#xff1a; 组件…

共享云桌面为什么能打败传统电脑

近年来&#xff0c;随着云桌面技术的快速发展&#xff0c;共享云桌面作为一种新型的计算模式&#xff0c;正在逐步改变人们的工作和生活方式。它凭借其独特的优势&#xff0c;正在逐步取代传统电脑&#xff0c;成为企业和个人用户的新选择。之所以在部分场景中展现出替代传统电…

B站PWN教程笔记-12

完结撒花。 今天还是以做题为主。 fmtstruaf 格式化字符串USER AFTER FREE 首先补充一个背景知识&#xff0c;指针也是有数据类型的&#xff0c;不同数据类型的指针xx&#xff0c;所加的字节数也不一样&#xff0c;其实是指针指的项目的下一项。如int a[20]&#xff0c;a是…

零基础设计模式——总结与进阶 - 3. 学习资源与下一步

第五部分&#xff1a;总结与进阶 - 3. 学习资源与下一步 到这里&#xff0c;你已经完成了设计模式主要内容的学习。但这仅仅是一个开始&#xff0c;设计模式的精髓在于实践和持续学习。本节将为你提供一些优质的学习资源和后续学习的建议&#xff0c;帮助你在这条道路上走得更…