periphery

periphery

Swift项目未使用代码识别工具

Periphery是一款识别Swift项目中未使用代码的开源工具。它能分析项目结构,找出未引用的声明、未使用的函数参数和冗余协议等。支持Xcode和Swift Package Manager项目,可通过Homebrew或Mint安装。Periphery提供命令行界面和Xcode集成,帮助开发者清理代码。其分析范围涵盖函数参数、协议、枚举和属性等多个方面,适用于Swift项目代码优化。

PeripherySwift代码分析未使用代码静态分析Github开源项目
<h1 align="center"> <img src="https://yellow-cdn.veclightyear.com/0a4dffa0/f71808f4-261c-4f5c-8526-71d5cddf0070.png" alt="Periphery" height="60" /> <br> Periphery </h1> <h4 align="center">一个用于识别 Swift 项目中未使用代码的工具。</h4> <p align="center"><q><i>现在我成为了删除者,代码的毁灭者。</i></q></p> <p align="center"> <a href="https://github.com/peripheryapp/periphery/releases/latest"> <img src="https://yellow-cdn.veclightyear.com/0a4dffa0/66885c02-2599-49e0-9e65-6a84a4415311.svg?color=008DFF"/></a> <img src="https://img.shields.io/badge/platform-macOS%20|%20Linux-008DFF"> <a href="#sponsors-"> <img src="https://img.shields.io/github/sponsors/peripheryapp?logo=githubsponsors&color=db61a2"> </a> <br> <br> </p>

目录

安装

Homebrew

brew install peripheryapp/periphery/periphery

Mint

mint install peripheryapp/periphery

使用方法

scan 命令

scan 命令是 Periphery 的主要功能。要开始引导式设置,只需切换到项目目录并运行:

periphery scan --setup

引导式设置仅适用于 Xcode 和 SwiftPM 项目,要在非苹果构建系统(如 Bazel)中使用 Periphery,请参阅构建系统

回答几个问题后,Periphery 将打印出完整的扫描命令并执行它。

引导式设置仅用于入门目的,一旦你熟悉了 Periphery,你可以尝试一些更高级的选项,所有这些选项都可以通过 periphery help scan 查看。

要从 Periphery 获得一致的结果,理解你选择分析的构建目标的影响至关重要。例如,假设一个项目由三个目标组成:App、Lib 和 Tests。App 目标导入 Lib,Tests 目标导入 App 和 Lib。如果你在 --targets 选项中提供所有三个目标,那么 Periphery 将能够分析你的整个项目。但是,如果你只选择分析 App 和 Lib,而不分析 Tests,Periphery 可能会报告一些未使用的代码实例,这些实例_仅_被 Tests 引用。因此,当你怀疑 Periphery 提供了不正确的结果时,考虑你选择分析的目标很重要。

如果你的项目由一个或多个独立的框架组成,这些框架不包含消费其接口的应用程序,那么你需要告诉 Periphery 假设所有公共声明实际上都被使用,方法是包含 --retain-public 选项。

对于混合了 Objective-C 和 Swift 的项目,强烈建议你阅读相关影响

配置

一旦你确定了适合你项目的选项,你可能希望将它们保存在 YAML 配置文件中。最简单的方法是使用 --verbose 选项运行 Periphery。在输出的开头附近,你会看到 [configuration:begin] 部分,下面是格式化为 YAML 的配置。将配置复制并粘贴到项目文件夹根目录的 .periphery.yml 中。现在你只需运行 periphery scan,YAML 配置就会被使用。

工作原理

Periphery 首先构建你的项目。对于 Xcode 项目,通过 --schemes 选项提供的方案使用 xcodebuild 构建。对于 Swift Package Manager 项目,通过 --targets 选项提供的单个目标使用 swift build 构建。Swift 编译器采用一种称为边构建边索引的技术来填充索引存储,其中包含有关项目源代码结构的信息。 项目构建完成后,Periphery 会执行索引阶段。对于通过 --targets 选项提供的目标中的每个源文件,Periphery 从索引存储中获取其结构信息,并构建项目的内部图形表示。Periphery 还分析每个文件的抽象语法树(AST),以填补索引存储未提供的一些细节。

索引完成后,Periphery 分析图形以识别未使用的代码。这个阶段包括一系列步骤,这些步骤会改变图形,使其更容易识别未使用代码的特定场景。最后一步是从图形的根部遍历,以识别不再被引用的声明。

分析

Periphery 的目标是报告未使用的声明实例。声明包括 classstructprotocolfunctionpropertyconstructorenumtypealiasassociatedtype 等。正如您所预期的,Periphery 能够识别简单的未引用声明,例如在代码库中不再使用的 class

Periphery 还可以识别更高级的未使用代码实例。以下部分详细解释了这些情况。

函数参数

Periphery 可以识别未使用的函数参数。未使用的参数实例也可以在协议及其遵循声明中识别,以及在重写方法中的参数。这两种情况将在下面进一步解释。

协议

只有当参数在所有实现中都未使用时,协议函数的未使用参数才会被报告为未使用。

protocol Greeter { func greet(name: String) func farewell(name: String) // 'name' 未使用 } class InformalGreeter: Greeter { func greet(name: String) { print("嗨 " + name + "。") } func farewell(name: String) { // 'name' 未使用 print("再见。") } }

提示

您可以使用 --retain-unused-protocol-func-params 选项忽略所有来自协议和遵循函数的未使用参数。

重写

类似于协议,只有当重写函数的参数在基函数和所有重写函数中都未使用时,才会被报告为未使用。

class BaseGreeter { func greet(name: String) { print("你好。") } func farewell(name: String) { // 'name' 未使用 print("再见。") } } class InformalGreeter: BaseGreeter { override func greet(name: String) { print("嗨 " + name + "。") } override func farewell(name: String) { // 'name' 未使用 print("拜。") } }

外部协议和类

外部模块(如 Foundation)中定义的协议或类的未使用参数始终被忽略,因为您无法修改基函数声明。

fatalError 函数

仅调用 fatalError 的函数中未使用的参数也会被忽略。这样的函数通常是子类中未实现的必需初始化器。

class Base { let param: String required init(param: String) { self.param = param } } class Subclass: Base { init(custom: String) { super.init(param: custom) } required init(param: String) { fatalError("init(param:) 尚未实现") } }

协议

一个被对象遵循的协议,只有在作为存在类型使用或用于特化泛型方法/类时才是真正被使用的。Periphery 能够识别这种冗余协议,无论它们被一个还是多个对象遵循。

protocol MyProtocol { // 'MyProtocol' 是冗余的 func someMethod() } class MyClass1: MyProtocol { // 'MyProtocol' 遵循是冗余的 func someMethod() { print("来自 MyClass1 的问候!") } } class MyClass2: MyProtocol { // 'MyProtocol' 遵循是冗余的 func someMethod() { print("来自 MyClass2 的问候!") } } let myClass1 = MyClass1() myClass1.someMethod() let myClass2 = MyClass2() myClass2.someMethod()

在这里我们可以看到,尽管 someMethod 的两个实现都被调用了,但在任何时候对象都没有采用 MyProtocol 类型。因此,协议本身是冗余的,MyClass1MyClass2 遵循它没有任何好处。我们可以移除 MyProtocol 以及每个冗余的遵循,只在每个类中保留 someMethod

就像对象的普通方法或属性一样,协议声明的各个属性和方法也可以被识别为未使用。

protocol MyProtocol { var usedProperty: String { get } var unusedProperty: String { get } // 'unusedProperty' 未使用 } class MyConformingClass: MyProtocol { var usedProperty: String = "已使用" var unusedProperty: String = "未使用" // 'unusedProperty' 未使用 } class MyClass { let conformingClass: MyProtocol init() { conformingClass = MyConformingClass() } func perform() { print(conformingClass.usedProperty) } } let myClass = MyClass() myClass.perform()

在这里我们可以看到,MyProtocol 本身是被使用的,不能被移除。然而,由于 unusedProperty 从未在 MyConformingClass 上被调用,Periphery 能够识别出 MyProtocolunusedProperty 的声明也未被使用,可以与未使用的 unusedProperty 实现一起移除。

枚举

除了能够识别未使用的枚举,Periphery 还可以识别单个未使用的枚举案例。可以可靠地识别不具有原始表示性的普通枚举,即那些没有 StringCharacterInt 或浮点值类型的枚举。然而,具有原始值类型的枚举可能具有动态性质,因此必须假定它们被使用。

让我们用一个简单的例子来说明这一点:

enum MyEnum: String { case myCase } func someFunction(value: String) { if let myEnum = MyEnum(rawValue: value) { somethingImportant(myEnum) } }

这里没有直接引用 myCase 案例,所以我们可能会认为它不再需要。但是,如果删除它,我们可以看到当 someFunction 传入 "myCase" 值时,somethingImportant 将永远不会被调用。

仅赋值属性

被赋值但从未使用的属性会被识别为仅赋值属性,例如:

class MyClass { var assignOnlyProperty: String // 'assignOnlyProperty' 被赋值,但从未使用 init(value: String) { self.assignOnlyProperty = value } }

在某些情况下,这可能是预期的行为,因此你有几个选项可以消除这类结果:

  • 使用注释命令保留单个属性。
  • 使用 --retain-assign-only-property-types 按类型保留所有仅赋值属性。给定的类型必须与属性声明中的确切用法匹配(不包括可选的问号),例如 String[String]Set<String>。Periphery 无法解析推断的属性类型,因此在某些情况下,你可能需要为属性添加显式类型注解。
  • 使用 --retain-assign-only-properties 完全禁用仅赋值属性分析。

冗余的公共可访问性

被标记为 public 但在其所在模块外部没有被引用的声明会被识别为具有冗余的公共可访问性。在这种情况下,可以从声明中移除 public 注解。移除冗余的公共可访问性有几个好处:

可以使用 --disable-redundant-public-analysis 禁用此分析。

未使用的导入

Periphery 可以检测它已扫描的目标(即用 --targets 参数指定的目标)中未使用的导入。它无法检测其他目标的未使用导入,因为无法获取 Swift 源文件,也无法观察 @_exported 的使用情况。@_exported 存在问题,因为它改变了目标的公共接口,使得目标导出的声明不再必须由导入的目标声明。例如,Foundation 目标导出了 Dispatch 等多个目标。如果任何给定的源文件导入 Foundation 并引用了 DispatchQueue,但没有引用 Foundation 中的其他声明,那么就不能移除 Foundation 导入,因为这也会使 DispatchQueue 类型不可用。因此,为了避免误报,Periphery 只检测它已扫描的目标中未使用的导入。

对于混合 Swift 和 Objective-C 的目标,Periphery 可能会产生误报,因为 Periphery 无法扫描 Objective-C 文件。因此,建议对具有大量 Objective-C 代码的项目禁用未使用导入检测,或手动从结果中排除混合语言目标。

Objective-C

Periphery 无法分析 Objective-C 代码,因为类型可能是动态类型。

默认情况下,Periphery 不假设 Objective-C 运行时可访问的声明正在使用。如果你的项目混合了 Swift 和 Objective-C,你可以使用 --retain-objc-accessible 选项启用此行为。可以被 Objective-C 运行时访问的 Swift 声明是那些显式注解了 @objc@objcMembers 的声明,以及直接或通过另一个类间接继承 NSObject 的类。

另外,可以使用 --retain-objc-annotated 选项仅保留显式注解了 @objc@objcMembers 的声明。继承 NSObject 的类型不会被保留,除非它们有显式注解。这个选项可能会发现更多未使用的代码,但缺点是如果声明实际上在 Objective-C 代码中使用,部分结果可能不正确。要解决这些不正确的结果,你必须为声明添加 @objc 注解。

Codable

Swift 为 Codable 类型合成额外的代码,这些代码对 Periphery 不可见,可能导致在非合成代码中未直接引用的属性出现误报。如果你的项目包含许多这样的类型,你可以使用 --retain-codable-properties 保留所有 Codable 类型的属性。或者,你可以使用 --retain-encodable-properties 仅保留 Encodable 类型的属性。

如果 Codable 一致性是由 Periphery 未扫描的外部模块中的协议声明的,你可以使用 --external-codable-protocols "ExternalProtocol" 指示 Periphery 将这些协议识别为 Codable

XCTestCase

任何继承自 XCTestCase 的类及其测试方法都会被自动保留。然而,当一个类通过另一个类间接继承 XCTestCase,例如 UnitTestCase,且该类位于 Periphery 不会扫描的目标中时,你需要使用 --external-test-case-classes UnitTestCase 选项来指示 Periphery 将 UnitTestCase 视为 XCTestCase 的子类。

Interface Builder

如果你的项目包含 Interface Builder 文件(如 storyboard 和 XIB),Periphery 在识别未使用的声明时会考虑这些文件。但是,Periphery 目前只能识别未使用的类。这个限制存在是因为 Periphery 尚未完全解析 Interface Builder 文件(参见 issue #212)。由于 Periphery 的设计原则是避免误报,所以假设如果一个类在 Interface Builder 文件中被引用,那么它的所有 IBOutletsIBActions 都被认为是在使用中,即使实际上可能并非如此。一旦 Periphery 具备解析 Interface Builder 文件的能力,这种方法将被修改以准确识别未使用的 IBActionsIBOutlets

注释命令

出于某些原因,你可能想保留一些未使用的代码。可以使用源代码注释命令来忽略特定声明,并将其从结果中排除。

可以在任何声明的上一行直接放置忽略注释命令,以忽略该声明及其所有后代声明:

// periphery:ignore class MyClass {}

你还可以忽略特定的未使用函数参数:

// periphery:ignore:parameters unusedOne,unusedTwo func someFunc(used: String, unusedOne: String, unusedTwo: String) { print(used) }

// periphery:ignore:all 命令可以放在源文件的顶部,以忽略文件的全部内容。请注意,该注释必须放在任何代码之上,包括导入语句。

注释命令还支持在连字符后添加尾随注释,这样你可以在同一行中包含解释:

// periphery:ignore - 解释为什么这是必要的 class MyClass {}

Xcode 集成

在设置 Xcode 集成之前,我们强烈建议你先在终端中使用 Periphery,因为你将通过 Xcode 使用完全相同的命令。

步骤 1:创建聚合目标

在项目导航器中选择你的项目,然后点击目标部分左下角的 + 按钮。选择跨平台并选择聚合。点击下一步。

步骤 1

为新目标选择一个名称,例如 "Periphery" 或 "未使用代码"。

步骤 2

步骤 2:添加运行脚本构建阶段

构建阶段部分,点击 + 按钮添加新的运行脚本阶段。

步骤 3

在 shell 脚本窗口中输入 Periphery 命令。确保包含 --format xcode 选项。

步骤 4

步骤 3:选择并运行

现在你已准备就绪。你应该能在下拉菜单中看到新的方案。选择它并点击运行。

提示

如果你希望团队中的其他人也能使用该方案,你需要将其标记为_共享_。可以通过选择_管理方案..._并选中新方案旁边的_共享_复选框来完成。现在可以将方案定义提交到源代码控制中。

步骤 5

排除文件

以下两个排除选项都接受 Bash v4 风格的路径通配符,可以是绝对路径或相对于你的项目目录的路径。你可以用空格分隔多个通配符,例如 --option "Sources/Single.swift" "**/Generated/*.swift" "**/*.{xib,storyboard}"

排除结果

要从某些文件中排除结果,请向 scan 命令传递 --report-exclude <globs> 选项。

排除索引文件

要从索引中排除文件,请向 scan 命令传递 --index-exclude <globs> 选项。从索引阶段排除文件意味着 Periphery 将看不到这些文件中包含的任何声明和引用。Periphery 的行为就像这些文件不存在一样。例如,此选项可用于排除包含对非生成代码引用的生成代码,或排除所有包含对代码引用的 .xib.storyboard 文件。

保留文件声明

要保留文件中的所有声明,请向 scan 命令传递 --retain-files <globs> 选项。此选项相当于在每个文件顶部添加 // periphery:ignore:all 注释命令。

持续集成

在将 Periphery 集成到 CI 流水线时,如果您的流水线已经完成构建(例如运行测试),您可以跳过构建阶段。这可以通过使用 --skip-build 选项来实现。但是,您还需要使用 --index-store-path 告诉 Periphery 索引存储的位置。此位置取决于您的项目类型。

请注意,使用 --skip-build--index-store-path 时,确保索引存储包含您通过 --targets 指定的所有目标的数据至关重要。例如,如果您的流水线之前构建了 'App' 和 'Lib' 目标,索引存储将只包含这些目标中文件的数据。然后您不能指示 Periphery 扫描额外的目标,如 'Extension' 或 'UnitTests'。

Xcode

xcodebuild 生成的索引存储位于 DerivedData 中,具体位置取决于您的项目,例如 ~/Library/Developer/Xcode/DerivedData/YourProject-abc123/Index/DataStore。对于 Xcode 14 及更高版本,Index 目录可能表示为 Index.noindex,这会抑制 Spotlight 索引。

SwiftPM

默认情况下,Periphery 在 .build/debug/index/store 查找索引存储。因此,如果您打算在调用 swift test 后直接运行 Periphery,可以省略 --index-store-path 选项,Periphery 将使用项目构建用于测试时创建的索引存储。但如果不是这种情况,则必须使用 --index-store-path 提供 Periphery 索引存储的位置。

构建系统

Periphery 可以分析使用第三方构建系统(如 Bazel)的项目,但不能像 SwiftPM 和 xcodebuild 那样自动驱动它们。相反,您需要指定索引存储位置并提供文件-目标映射文件。

文件-目标映射文件包含源文件到构建目标的简单映射。您需要使用构建系统的适当工具自行生成此文件。格式如下:

{ "file_targets": { "path/to/file_a.swift": ["TargetA"], "path/to/file_b.swift": ["TargetB", "TargetC"] } }

提示

相对路径假定相对于当前目录。

然后您可以如下调用 periphery:

periphery scan --file-targets-path map.json --index-store-path index/store

提示

这两个选项都支持多个路径。

平台

Periphery 支持 macOS 和 Linux。macOS 支持 Xcode 和 Swift Package Manager (SPM) 项目,而 Linux 仅支持 SPM 项目。

故障排除

一个或多个文件中的错误结果,如误报和不正确的源文件位置

索引存储可能会损坏或与源文件不同步。例如,如果您强制终止(^C)扫描,就可能发生这种情况。要纠正此问题,您可以向扫描命令传递 --clean-build 标志,强制删除现有的构建产物。

预处理器宏条件分支内引用的代码未使用

当 Periphery 构建您的项目时,它使用默认的构建配置,通常是 'debug'。如果您使用预处理器宏来有条件地编译代码,Periphery 将只能看到被编译的分支。在下面的示例中,releaseName 将被报告为未使用,因为它只在宏的非调试分支中被引用。

struct BuildInfo { let debugName = "debug" let releaseName = "release" // 'releaseName' 未使用 var name: String { #if DEBUG debugName #else releaseName #endif } }

您有几个选项来解决这个问题:

  • 使用注释命令显式忽略 releaseName
  • 过滤结果以删除已知实例。
  • 为每个构建配置运行一次 Periphery 并合并结果。您可以在 -- 后指定传递给底层构建的参数,例如 periphery scan ... -- -configuration release

Swift 包是平台特定的

Periphery 使用 swift build 编译 Swift 包,如果 Swift 包是平台特定的(例如针对 iOS),这将失败。

作为解决方法,您可以手动使用 xcodebuild 构建 Swift 包,然后使用 --skip-build--index-store-path 选项来针对 xcodebuild 之前生成的索引存储。

示例:

# 1. 使用 xcodebuild xcodebuild -scheme MyScheme -destination 'platform=iOS Simulator,OS=16.2,name=iPhone 14' -derivedDataPath '../dd' clean build # 2. 使用生成的索引存储进行扫描 periphery scan --skip-build --index-store-path '../dd/Index.noindex/DataStore/'

已知问题

由于 Swift 中的一些底层错误,Periphery 在某些情况下可能会报告不正确的结果。

ID标题
56559索引存储未关联通过 Self 引用的构造函数
56541索引存储未关联用作下标键的静态属性获取器
56327索引存储未关联子类中实现的 objc 可选协议方法
56189索引存储应关联字符串字面量中的 appendInterpolation
56165索引存储未关联通过字面量表示法的构造函数
49641索引不包含对具有泛型类型的类/结构体构造函数的引用

赞助商 赞助商

Periphery 是一个需要大量精力维护和开发的热情项目。如果您觉得 Periphery 有用,请考虑通过 GitHub Sponsors 进行赞助。

特别感谢以下慷慨的赞助商:

Emerge Tools

Emerge Tools 是一套革命性产品,旨在为移动应用及其开发团队提供超强动力。

<a href="https://www.emergetools.com" alt="Emerge Tools"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://github.com/peripheryapp/periphery/raw/master/assets/sponsors/emerge-tools-vertical-white.svg"> <source media="(prefers-color-scheme: light)" srcset="https://yellow-cdn.veclightyear.com/0a4dffa0/1874f002-f304-4eb9-8f2c-7a0e6eb10e41.svg"> <img src="https://yellow-cdn.veclightyear.com/0a4dffa0/1874f002-f304-4eb9-8f2c-7a0e6eb10e41.svg"> </picture> </a>

编辑推荐精选

TRAE编程

TRAE编程

AI辅助编程,代码自动修复

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
蛙蛙写作

蛙蛙写作

AI小说写作助手,一站式润色、改写、扩写

蛙蛙写作—国内先进的AI写作平台,涵盖小说、学术、社交媒体等多场景。提供续写、改写、润色等功能,助力创作者高效优化写作流程。界面简洁,功能全面,适合各类写作者提升内容品质和工作效率。

AI辅助写作AI工具蛙蛙写作AI写作工具学术助手办公助手营销助手AI助手
问小白

问小白

全能AI智能助手,随时解答生活与工作的多样问题

问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。

热门AI助手AI对话AI工具聊天机器人
Transly

Transly

实时语音翻译/同声传译工具

Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

AI办公办公工具AI工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图热门
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

热门AI开发模型训练AI工具讯飞星火大模型智能问答内容创作多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

咔片PPT

咔片PPT

AI助力,做PPT更简单!

咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

热门AI辅助写作AI工具讯飞绘文内容运营AI创作个性化文章多平台分发AI助手
材料星

材料星

专业的AI公文写作平台,公文写作神器

AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。

下拉加载更多