用于调试、比较和测试应用程序数据结构的工具集合。
Swift 提供了一个很棒的工具 dump
,可以将任何值的内容转储为字符串。它将值的所有字段和子字段打印成树状描述:
struct User { var favoriteNumbers: [Int] var id: Int var name: String } let user = User( favoriteNumbers: [42, 1729], id: 2, name: "Blob" ) dump(user)
▿ User ▿ favoriteNumbers: 2 elements - 42 - 1729 - id: 2 - name: "Blob"
这非常有用,可以用来构建可视化应用程序运行时值数据的调试工具,但有时它的输出并不理想。
例如,转储字典会导致冗长的输出,可能难以阅读(另外请注意键是无序的):
dump([1: "one", 2: "two", 3: "three"])
▿ 3 key/value pairs ▿ (2 elements) - key: 2 - value: "two" ▿ (2 elements) - key: 3 - value: "three" ▿ (2 elements) - key: 1 - value: "one"
同样,枚举的输出也非常冗长:
dump(Result<Int, Error>.success(42))
▿ Swift.Result<Swift.Int, Swift.Error>.success - success: 42
在处理深层嵌套结构时,它变得更难阅读:
dump([1: Result<User, Error>.success(user)])
▿ 1 key/value pair ▿ (2 elements) - key: 1 ▿ value: Swift.Result<User, Swift.Error>.success ▿ success: User ▿ favoriteNumbers: 2 elements - 42 - 1729 - id: 2 - name: "Blob"
有时 dump
根本不打印有用的信息,比如从 Objective-C 导入的枚举:
import UserNotifications dump(UNNotificationSetting.disabled)
- __C.UNNotificationSetting
因此,尽管 dump
函数很方便,但它通常是一个过于粗糙的工具。这就是 customDump
函数的动机。
customDump
customDump
函数模仿 dump
的行为,但提供了更精细的嵌套结构输出,优化了可读性。例如,结构体的转储格式更接近 Swift 中的结构体语法,数组的转储包含每个元素的索引:
import CustomDump customDump(user)
User( favoriteNumbers: [ [0]: 42, [1]: 1729 ], id: 2, name: "Blob" )
字典以更紧凑的格式转储,模仿 Swift 的语法,并自动排序键:
customDump([1: "one", 2: "two", 3: "three"])
[ 1: "one", 2: "two", 3: "three" ]
同样,枚举也以更紧凑、可读的格式转储:
customDump(Result<Int, Error>.success(42))
Result.success(42)
深层嵌套结构有一个简化的树状结构:
customDump([1: Result<User, Error>.success(user)])
[ 1: Result.success( User( favoriteNumbers: [ [0]: 42, [1]: 1729 ], id: 2, name: "Blob" ) ) ]
diff
使用 customDump
函数的输出,我们可以构建一个非常轻量级的方法来文本比较 Swift 中的任意两个值:
var other = user other.favoriteNumbers[1] = 91 print(diff(user, other)!)
User( favoriteNumbers: [ [0]: 42, - [1]: 1729 + [1]: 91 ], id: 2, name: "Blob" )
此外,当结构的部分没有变化时,会进行额外的工作以最小化差异的大小,例如大集合中的单个元素发生变化:
let users = (1...5).map { User( favoriteNumbers: [$0], id: $0, name: "Blob \($0)" ) } var other = users other.append( .init( favoriteNumbers: [42, 1729], id: 100, name: "Blob Sr." ) ) print(diff(users, other)!)
[ … (4 unchanged), + [4]: User( + favoriteNumbers: [ + [0]: 42, + [1]: 1729 + ], + id: 100, + name: "Blob Sr." + ) ]
对于一个真实的用例,我们修改了 Apple 的 Landmarks 教程应用程序,以打印收藏地标时的前后状态:
[ [0]: Landmark( id: 1001, name: "Turtle Rock", park: "Joshua Tree National Park", state: "California", description: "This very large formation lies south of the large Real Hidden Valley parking lot and immediately adjacent to (south of) the picnic areas.", - isFavorite: true, + isFavorite: false, isFeatured: true, category: Category.rivers, imageName: "turtlerock", coordinates: Coordinates(…) ), … (11 unchanged) ]
expectNoDifference
XCTest 的 XCTAssertEqual
和 Swift Testing 的 #expect(_ == _)
都允许你断言两个值相等,如果不相等,测试套件将失败并显示消息:
var other = user other.name += "!" XCTAssertEqual(user, other) #expect(user == other)
XCTAssertEqual失败:"User(favoriteNumbers: [42, 1729], id: 2, name: "Blob")"不等于"User(favoriteNumbers: [42, 1729], id: 2, name: "Blob!")" 期望失败:(user → User(favoriteNumbers: [42, 1729], id: 2, name: "Blob")) == (other → User(favoriteNumbers: [42, 1729], id: 2, name: "Blob!"))
不幸的是,这些失败信息很难直观地解析和理解。需要花几秒钟在信息中寻找才能发现唯一的区别是名称末尾的感叹号。如果类型更复杂,包含嵌套结构和大型集合,问题会变得更糟。
该库还附带了一个expectNoDifference
函数来缓解这些问题。它的工作方式类似于XCTAssertEqual
和#expect(_ == _)
,只是失败信息使用格式良好的差异来准确显示两个值之间的不同:
expectNoDifference(user, other)
expectNoDifference失败:… User( favoriteNumbers: [...], id: 2, - name: "Blob" + name: "Blob!" ) (第一个:-,第二个:+)
expectDifference
该函数提供了expectNoDifference
的反向功能:它通过在给定操作之前和之后评估给定表达式,然后比较结果,来断言一个值有一组变化。
例如,给定一个非常简单的计数器结构,我们可以针对其递增功能编写测试:
struct Counter { var count = 0 var isOdd = false mutating func increment() { self.count += 1 self.isOdd.toggle() } } var counter = Counter() expectDifference(counter) { counter.increment() } changes: { $0.count = 1 $0.isOdd = true }
如果changes
没有详尽描述所有changed字段,断言将失败。
通过省略操作,你可以在changes
闭包中只描述你想断言的字段,从而对一个值进行"非详尽"断言:
counter.increment() expectDifference(counter) { $0.count = 1 // 不需要进一步描述`isOdd`如何变化 }
Custom Dump提供了几种重要的方式来自定义数据类型的转储方式:CustomDumpStringConvertible
、CustomDumpReflectable
和CustomDumpRepresentable
。
CustomDumpStringConvertible
CustomDumpStringConvertible
协议提供了一种简单的方法,将类型转换为原始字符串以进行转储。它最适合具有简单、非嵌套内部表示的类型,其输出通常适合单行显示,例如日期、UUID、URL等:
extension URL: CustomDumpStringConvertible { public var customDumpDescription: String { "URL(\(self.absoluteString))" } } customDump(URL(string: "https://www.pointfree.co/")!)
URL(https://www.pointfree.co/)
Custom Dump还在内部使用此协议为从Objective-C导入的枚举提供更有用的输出:
import UserNotifications print("dump:") dump(UNNotificationSetting.disabled) print("customDump:") customDump(UNNotificationSetting.disabled)
dump: - __C.UNNotificationSetting customDump: UNNotificationSettings.disabled
遇到打印不美观的Objective-C枚举?请参阅本README的贡献部分以帮助提交修复。
CustomDumpReflectable
CustomDumpReflectable
协议提供了一种更全面的方法来将类型转储为更结构化的输出。它允许你构造一个自定义镜像,描述应该转储的结构。你可以省略、添加和替换字段,甚至更改结构转储的"显示样式"。
例如,假设你有一个表示状态的结构,它在内存中保存了一个不应该写入日志的安全令牌。你可以通过提供一个省略此字段的镜像来从customDump
中省略令牌:
struct LoginState: CustomDumpReflectable { var username: String var token: String var customDumpMirror: Mirror { .init( self, children: [ "username": self.username, // 从日志中省略令牌 ], displayStyle: .struct ) } } customDump( LoginState( username: "blob", token: "secret" ) )
LoginState(username: "blob")
就这样,转储中不会写入令牌数据。
CustomDumpRepresentable
CustomDumpRepresentable
协议允许你返回_任何_值用于转储。这对于扁平化 包装器类型的转储表示很有用。例如,类型安全标识符可能希望直接转储其原始值:
struct ID: RawRepresentable { var rawValue: String } extension ID: CustomDumpRepresentable { var customDumpValue: Any { self.rawValue } } customDump(ID(rawValue: "deadbeef")
"deadbeef"
Apple生态系统中有许多类型无法转储为格式良好的字符串。特别是,从Objective-C导入的所有枚举都会转储为不太有用的字符串:
import UserNotifications dump(UNNotificationSetting.disabled)
- __C.UNNotificationSetting
因此,我们已经让Apple的许多类型符合CustomDumpStringConvertible
协议,以便它们能打印出更合理的描述。如果你遇到不打印有用信息的类型,我们很乐意接受PR让这些类型符合CustomDumpStringConvertible
。
你可以通过将Custom Dump添加为包依赖项来将其添加到Xcode项目中。
如果你想在SwiftPM项目中使用Custom Dump,只需将其添加到Package.swift
中的dependencies
子句即可:
dependencies: [ .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.0.0") ]
Custom Dump API的最新文档可在此处获得。
该库根据MIT 许可证发布。有关详细信息,请参阅LICENSE。
字节跳动发布的AI编程神器IDE
Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。
全能AI智能助手,随时解答生活与工作的多样问题
问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个 人事务。
实时语音翻译/同声传译工具
Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线 上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。
一键生成PPT和Word,让学习生活更轻松
讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。
深度推理能力全新升级,全面对标OpenAI o1
科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为 不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。
一 种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型
Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。
AI助力,做PPT更简单!
咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。
选题、配图、成文,一站式创作,让内容运营更高效
讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。
专业的AI公文写作平台,公文写作神器
AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。
OpenAI Agents SDK,助力开发者便捷使用 OpenAI 相关功能。
openai-agents-python 是 OpenAI 推出的一款强大 Python SDK,它为开发者提供了与 OpenAI 模型交互的高效工具,支持工具调用、结果处理、追踪等功能,涵盖多种应用场景,如研究助手、财务研究等,能显著提升开发效率,让开发者更轻松地利用 OpenAI 的技术优势。
最新AI工具、AI资讯
独家AI资源、AI项目落地
微信扫一扫关注公众号