游乐场可以让你看到你的Swift代码在每一步都在做什么。每次执行语句时,其结果都会沿着右侧记录到侧边栏中。从那里,您可以在弹出窗口中打开结果的快速查看预览,或者直接在代码编辑器中内联显示结果。
负责提供这种反馈的代码是由**Playground Logger框架提供的,它是开放源代码swif t-xcode-playground-support**项目的一部分。
通过阅读代码,我们了解到游乐场记录器区分结构化值和不透明值,前者的状态通过检查其内部成员来披露,后者提供了自身的专门表示。除了这两个之外,记录器还识别范围(控制流语句、函数等)的入口和出口点,以及运行时错误(由隐式解压缩零值、致命错误()等引起)。其他任何东西——导入、分配、空行——都被认为是空白
内置不透明表示
游乐场记录器为您可能在Foundation,UI Kit,AppKit,Sprite Kit,Core Graphics,Core Image和Swift标准库中与之交互的许多类型提供了内置的不透明表示:
CategoryTypesResultStringsStringNSString"Hello, world!"Attributed StringsNSAttributedString"Hello, world!"NumbersInt, UInt, …Double, Float, …CGFloatNSNumber42RangesNSRange{0, 10}Boolean ValuesBoolTRUEPointersUnsafePointerUnsafeMutablePointerUnsafeRawPointerUnsafeMutableRawPointer0x0123456789ABCDEFDatesDateNSDateNov 12, 2018 at 10:00URLsURLNSURLhttps://nshipster.comColorsCGColorNSColorUIColorCIColor🔴 r 1.0 g 0.0 b 0.0 a 1.0 GeometryCGPointCGSizeCGRect{x 0 y 0 w 100 h 100} Bezier PathsNSBezierPathUIBezierPath11 path elementsImagesCGImageNSCursorNSBitmapImageRepNSImageUIImagew 50 h 50SpriteKit NodesSKShapeNodeSKSpriteNodeSKTextureSKTextureAtlasSKShapeNode ViewsNSViewUIViewNSView
结构化价值观
或者,游乐场记录器提供了以结构表示的值——而不需要自定义反射**协议**的实现。
如果值是元组、枚举用例或类或结构的实例,则可以这样做。它处理聚合,或从目标C类桥接的值,以及容器,如数组和字典。如果该值是可选的,则记录器将隐式地打开其值(如果存在)。
自定义在游乐场中记录结果的方式
开发人员可以通过扩展类型来自定义游乐场记录器显示结果的方式,以采用自定义游乐场显示转换协议并实现所需的游乐场描述计算属性。
例如,假设您正在使用游乐场来熟悉联系人框架。*(注意:在适用于iPad的Swift Playground中,联系人框架不可用)*您可以创建一个新的CN Mutable联系人,设置given Name和family Name属性,并为email Address属性提供一个CN Label ed Value值数组:
import Contactslet contact = CNMutableContact()contact.givenName = "Johnny"contact.familyName = "Appleseed"contact.emailAddresses = [ CNLabeledValue(label: CNLabelWork, value: "johnny@apple.com")]
如果您希望得到反馈来验证您的应用编程接口使用情况,您会对结果侧边栏中显示的内容感到失望:
<CNMutableContact: 0x7ff727e38bb0: ... />
为了改进这一点,我们可以扩展CN Mutable Access的超类,并使其符合Custom Play ground Display Converble。该联系人框架包括CN Contac tFor ane,它提供了一种方便的方法来总结联系人:
extension CNContact: CustomPlaygroundDisplayConvertible { public var playgroundDescription: Any { return CNContactFormatter.string(from: self, style: .fullName) ?? "" }}
通过将此放在我们游乐场的顶部(或游乐场辅助资源中的单独文件),我们以前的联系人现在提供了一个更好的快速浏览表示:
"约翰尼·苹果籽"
若要提供专用的Playground表示,请委托给上表中列出的值类型之一。在这种情况下,触点UI框架提供了一个CN Contac tView Controller类,我们可以在这里使用它的视图属性*(恼人的是,ios和mac OS之间的应用编程接口略有不同,因此编译器指令*):
import Contactsimport ContactsUIextension CNContact: CustomPlaygroundDisplayConvertible { public var playgroundDescription: Any { let viewController: CNContactViewController #if os(macOS) viewController = CNContactViewController() viewController.contact = self #elseif os(iOS) viewController = CNContactViewController(for: self) #else #warning("ContactsUI unavailable") #endif return viewController.view }}
替换了我们原来的play ground Description实现后,我们的联系人会显示以下UI:
游乐场在Xcode工具生态系统中占据了一个有趣的空间。它既不是主要的调试接口,也不是与最终用户通信的机制。相反,它利用低级和面向用户的功能来提供更丰富的开发体验。正因为如此,很难理解游乐场是如何与其他一切相适应的。
以下是一些相关功能的概要:
自定义字符串转换和自定义调试字符串转换的关系
游乐场记录器在确定如何在结果侧边栏中表示值时使用以下标准:
-
如果值是一个字符串,返回该值
-
如果值是Custom String Converble或Custom Debug String Converble,则返回String(反映:)
-
如果该值是枚举(由Mirror确定),则返回String(描述:)
-
否则,返回类型名,规范化以从完全限定名中删除模块
因此,您可以通过提供与自定义字符串转换或自定义调试字符串转换的一致性来自定义类型的游乐场描述。
所以问题变成了,“我如何决定采用这些协议中的哪一个?”
以下是一些一般准则:
-
使用自定义字符串可转换(描述)以适合用户的方式表示值。
-
使用自定义调试字符串转换(debug Description)以适合开发人员的方式表示值。
-
使用自定义游乐场显示可转换(游乐场描述)以适合游乐场上下文中开发人员的方式表示值。
在游乐场中,表现力优先于原始执行。因此,我们在生成描述所需的工作量方面有一定的回旋余地。
例如,大多数序列的默认表示形式是类型名(通常带有隐晦的泛型约束):
let evens = sequence(first: 0, next: {$0 + 2})
Unfold Sequence<Int,(可选,布尔)>
迭代序列具有未知的性能特性,因此将其包含在描述或调试描述中是不合适的。但是在操场上?*当然,发疯吧——*通过将它与游乐场本身联系起来,代码投入生产的风险很小。
所以回到我们最初的例子,让我们看看定制游戏显示转换如何帮助我们破译我们的序列:
extension UnfoldSequence: CustomPlaygroundDisplayConvertible where Element: CustomStringConvertible{ public var playgroundDescription: Any { return prefix(10).map{$0.description} .joined(separator: ", ") + "…" }}
0, 2, 4, 6, 8, 10, 12, 14, 16, 18…
与调试的关系快速查看
当游乐场记录结构化值时,它提供了一个类似于在调试模式下运行Xcode项目时所找到**的**界面。
这在实践中意味着,当使用结构化类型时,游乐场可以近似调试器界面。
例如,数据值的描述并没有告诉我们太多:
let data = "Hello, world!".data(using: .utf8)
来自描述
13字节
而且理由充分!正如我们在前一节中描述的那样,我们希望保持描述的实现良好而快速。
相比之下,从游乐场观看时,相同数据对象的结构化表示告诉我们大小和内存地址——它甚至显示了长度高达64的内联字节数组:
[72,101,108,108,111,44,32,119,111,114,108,100,33]
游乐场使用语言功能和工具的组合来提供实时、交互式的开发环境。使用Custom Play ground Display Converble协议,您可以为自己的类型利用这种内省。