CustomStringConvertible协议简介
CustomStringConvertible 是 Swift 的一个协议,用于自定义类型的字符串表示形式。通过实现这个协议,你可以决定一个对象在打印、日志记录或调试时的输出内容。
协议定义
CustomStringConvertible 只有一个属性:
protocol CustomStringConvertible {
var description: String { get }
}
description 属性:返回类型的自定义字符串描述。
默认行为:如果你不实现 description,打印对象时会使用系统生成的表示(通常包含类型和内存地址,如 MyType: 0x600003f4e0)。
使用场景
1、调试和日志输出:自定义打印类型的信息,便于调试。
2、显示人类可读的信息:让对象更具可读性。
3、与集合等配合使用:当对象被加入集合类型(如 Array 或 Dictionary)时,自动使用自定义描述。
简单示例
基础实现
struct User: CustomStringConvertible {
var id: Int
var name: String
// 自定义打印信息
var description: String {
return "User(id: \(id), name: \(name))"
}
}
let user = User(id: 1, name: "Alice")
print(user) // 输出: User(id: 1, name: Alice)
更复杂的示例
struct Task: CustomStringConvertible {
var title: String
var dueDate: Date
var isCompleted: Bool
var description: String {
let status = isCompleted ? "✅ Completed" : "❌ Pending"
return """
Task: \(title)
Due: \(dueDate)
Status: \(status)
"""
}
}
let task = Task(title: "Finish project", dueDate: Date(), isCompleted: false)
print(task)
/* 输出:
Task: Finish project
Due: 2024-11-13 10:00:00 +0000
Status: ❌ Pending
*/
注意事项
1、避免递归问题
如果 description 中打印对象自身或直接引用自身可能会导致递归调用并崩溃。
2、优化多行输出
如果有多行内容,用 “”” 多行字符串可以提升可读性。
3、调试友好
如果你需要不同的调试输出,可以结合 CustomDebugStringConvertible 协议。
实际应用
在项目中,配置SwiftData、iCloud以后,想要在视图中打印一个@Model属性包装器修饰的Friendface类型的实例。
@Model
class Friendface {
var id: String = "0"
var isActive: Bool = true
var name: String = "fangjunyu"
var age: Int = 27
var email: String = "fangjunyu.com@gmail.com"
var address: String = "山东省日照市"
var about: String = "关于SwiftData"
var registered: Date = Date.now
var tags: [String] = []
// 定义 Friendface 和 Friend 的关系
@Relationship(deleteRule: .cascade) var friends: [Friend]? = [] // 一对多
init(id: String = "0", isActive: Bool = true, name: String = "fangjunyu", age: Int = 27, email: String = "fangjunyu.com@gmail.com", address: String = "山东省日照市", about: String = "关于SwiftData", registered: Date = Date.now, tags: [String] = [], friends: [Friend] = []) {
self.id = id
self.isActive = isActive
self.name = name
self.age = age
self.email = email
self.address = address
self.about = about
self.registered = registered
self.tags = tags
self.friends = friends
}
}
在视图中尝试通过在onAppear方法中,输出Friendface类型的变量内容。
struct FriendInfo: View {
@Environment(\.modelContext) var modelContext
var friendInfo: Friendface
var body: some View {
Form {
... // 其他代码
}
.onAppear{
print("friendInfo:\(friendInfo)")
}
}
}
实际上输出的内容为:
friendInfo:hackingwithswift.Friendface
问题原因为:在Swift 中,默认的 print 输出会调用对象的 CustomStringConvertible 或 CustomDebugStringConvertible 协议。如果Friendface结构体没有实现这些协议,print(friendInfo) 将只输出对象的类型名称,而不会打印具体字段的内容。
因此,为了让print(friendInfo) 输出对象的具体字段内容,需要为 Friendface 实现 CustomStringConvertible 协议。
class Friendface: CustomStringConvertible {
var description: String {
"""
Friendface:
id: \(id)
isActive: \(isActive)
name: \(name)
age: \(age)
email: \(email)
address: \(address)
about: \(about)
registered: \(registered)
tags: \(tags)
"""
}
... // 其他代码
}
实现CustomStringConvertible协议后,就可以在类中定义description变量,并且可以在视图中通过Print输出对应的文字内容。
friendInfo:Friendface:
id: aa1f8001-59a3-4b3c-bf5e-4a7e1d8563f2
isActive: true
name: Pauline Dawson
age: 31
email: paulinedawson@hopeli.com
address: 263 Bokee Court, Brethren, Illinois, 2728
about: Nostrud officia nulla eu aute ipsum voluptate est sint exercitation adipisicing. Cupidatat adipisicing ipsum cupidatat anim velit Lorem mollit amet ipsum aliquip. Sit consectetur mollit laborum labore aliquip pariatur eu elit nisi velit non velit nisi deserunt. Dolor in et anim proident.
registered: 2015-07-25 05:04:07 +0000
tags: ["sunt", "enim", "cillum", "do", "ut", "nostrud", "mollit"]
总结
1、实现 CustomStringConvertible 协议的 description 属性,可以自定义类型的可读字符串表示。
2、适用于调试、日志记录或用户界面的友好输出。
3、如果需要为调试器提供单独的描述,使用 CustomDebugStringConvertible 协议。
扩展:CustomDebugStringConvertible
CustomDebugStringConvertible 是专门为调试器准备的协议,允许你为调试环境自定义另一种描述信息。
protocol CustomDebugStringConvertible {
var debugDescription: String { get }
}
示例:
struct Product: CustomStringConvertible, CustomDebugStringConvertible {
var name: String
var price: Double
var description: String {
return "Product(name: \(name), price: \(price))"
}
var debugDescription: String {
return "DEBUG: \(name) costs \(price) USD"
}
}
let product = Product(name: "MacBook", price: 1299.99)
print(product) // 输出: Product(name: MacBook, price: 1299.99)
debugPrint(product) // 输出: DEBUG: MacBook costs 1299.99 USD