Xcode 调试技巧之 Swift 篇

调试iBcker • 于 2015-09-30 08:43:58 +0800 • 最后由 dugege2015-09-30 18:49:26 +0800 9692 阅读

file

Swift开发的调试与OC类似,但也有很多不同之处,充分掌握一门语言的调试技巧对快速开发有很大帮助,使我们在开发过程中更加游刃有余,本文只讲Swift调试和OC的差异,并不是LLDB调试基础篇,还不了解的可以先看这里 Xcode 调试技巧之 OC 篇

可爱的class去哪了?

以前总喜欢po a.classpo a.superclass来查看类结构,但是在对swift代码做如下操作时····

``` var a:String?

--

(lldb) po a.class error: :1:3: error: expected member name following '.' a.class ^ :2:1: error: expected identifier in class declaration } ^ ```

困惑时先来看下String的定义:

public struct String { /// An empty `String`. public init() }

在Swift中String是个结构体,并没有像OC里的String那样是个类并继承于NSObject(class方法属于NSObject类)。并且String中自己也并没有实现class方法,所以Swift中自然就不能这么玩了~正确的姿势是:

(lldb) po a.dynamicType Swift.Optional<Swift.String>

什么鬼,第一眼根本看不懂····不过总比报错好了,仔细看下,a类型为Swift的Optional,里面装Swift的String

nil带来的困惑

(lldb) po a nil OC 时代,po输出这样的结果很常见,但是到了swift时代,这就有点烧脑,为什么这么说呢?因为Swift 中无论变量或是常量,都不能被赋予 nil ,所以,此货并非 nil

例如

``` (lldb) po a.dynamicType Swift.Optional

(lldb) e a="hello" (lldb) po a ▿ Optional("hello") - Some : "hello" ``` 那么当 a 赋值 nil 后,po a 是不是要打印 Optional(nil) 比较好理解一点?

p命令的改动

以前要获取一个对象self的内存地址,往往是用p self即可,如今改成了直接打印类结构了,充当了以前p *self的效果

(Demo.AppDelegate) $R0 = 0x00007f9feca2ab90 { UIKit.UIResponder = { NSObject = { isa = Demo.AppDelegate } _hasAlternateNextResponder = false _hasInputAssistantItem = false ...

取代原来的方案是unsafeAddressOf:

(lldb) po unsafeAddressOf(self) ▿ 0x00007f9feca2ab90 - pointerValue : 140324846611344

或者用NSLog

(lldb) po NSLog("%p",a)

但是!等等,这个对字符串变量并不适用,原因很简单,结构体传入方法时是传值,所以内存地址自然就是不对的。所以对于字符串,正确的内存地址获取方式是

(lldb) po str._core._baseAddress

感觉麻烦了不少啊啊啊啊啊啊啊啊····

打印类结构失效?

OC环境下打印对象类结构时可用p *a 到了Swift下··报错~

(lldb) p *self error: <EXPR>:1:1: error: '*' is not a prefix unary operator *self ^

相应的转变是改为直接p就好了

(lldb) p self (Demo.AppDelegate) $R87 = 0x00007fd22be0fba0 { UIKit.UIResponder = { NSObject = { isa = Demo.AppDelegate } _hasAlternateNextResponder = false _hasInputAssistantItem = false ...

打印时带Optional很烦人?

Swift打印Optional变量时会带上Optional("xxx"),除非是nil

```

var a :String? = "hello"

(lldb) po a ▿ Optional("hello") - Some : "hello" ```

这时候如果想去除Optional的干扰,可以强制解包即可

```

var a :String? = "hello"

(lldb) po a! "hello" ```

fatal error: unexpectedly found nil while unwrapping an Optional value

```

var a :String?

(lldb) po a! fatal error: unexpectedly found nil while unwrapping an Optional value error: Execution was interrupted, reason: EXCBADINSTRUCTION (code=EXCI386INVOP, subcode=0x0). The process has been returned to the state before expression evaluation. ``` 此错误为解包时遇到nil造成,所以代码了胡乱加上!解包其实有风险的,一定要想清楚是否会出现nil的情况

如何通过地址打印对象?

OC中很简单,直接po 0x000000011db11b80 即可,但这招在swift里不好使了·······

也不知道是不是bug···

不过还是可以通过间接点的手段完成

``` (lldb) po self

(lldb) e -l objc++ -O -- 0x7ffbb841bfe0

(lldb) e -l objc++ -O -- [0x7fa668f10b10 window] ; layer = > ```

对,你没看错,就是通过指定OC来调用···说句实话,变麻烦了,也不知道后是不是Xcode暂时的缺陷··

直接po地址无效的前提是当前调用栈在swift领域,并且我测试的版本是7.01,如果你有兴趣,可以尝试等程序跑去了后去暂停程序再po 地址,发现是好使的~什么?你想说这功能没用?NO!NO!NO!NO!很有用,后面会专门写一篇文章介绍这些技巧在生产中的用法

let/var导致类型难以判断?

swift的类型推断让语法在很多地方能省去了声明类型,这对开发者来说是好事,比较重复劳动就应该又机器来做。但是很多人开始抱怨说let/var定义让常量/变量不明确,有时候自己都的反应一会才能搞清楚他是个什么类型。举个例子:

let v = myMethod([1,2])

此段代码,如果不看myMethod的定义,你能知道v是什么类型么?并不能~这时候有个简单的办法可以查看 Option + Click 即可

file

expression中定义变量的变化

OC中为

(lldb) e NSString *$a = @"c" (lldb) po $a c

Swift中变为

(lldb) e let $a = "a" (lldb) po $a "a"

Swift的符号断点

OC中的符号断点为-/+[ClassName methodName],到了Swift环境里,坑爹的提示一没变··

file

感觉有点误导人,其实Swift里的符号表断点应该这么玩

file

此方法同样适用于swift里的结构体方法,函数,类方法等,不过比较有意思的是,类方法和实例方法的符号断点竟然完全一样,那如果只想给示例方法打断点该咋办?加条件?一这点和OC截然不同,OC里可以通过+/-号区分。

异常断点

很遗憾,异常断点还没加入对swift的支持,手动抛的异常并不能被断点捕获

file

断点行为 (Action)

Swift的行为断点已经支持直接写swift代码,可以通过条件决定是否要断下来

file

被忽略的REPL

Xcode 6.1 后REPL被集成进了xcode debuger中并能和调试环境互动了,由于篇幅问题,这里先不展开说,后面会专门写一篇介绍

总结

尽管类似得调试命令,不过通过对比还是不难看出有很多地方不同并且有很多不完善的地方,调试过程中也经常伴随crash,不顾毕竟OC改了那么多年得bug才走到今天,Swift有这个成绩已经不错了,再给些时日相信Swift会慢慢走向成熟稳定~

目前我能想到的不同点就这些,欢迎大家补充~

本帖已被设为精华帖!
本帖已被设为社区 Wiki!
回复: 2
  • zhuipiaochen 2015-09-30 17:40:05 +0800

    周董,nb,受教了!!

  • dugege 2015-09-30 18:49:26 +0800

    写得好赞,最近正好遇到不懂的···

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,见 Emoji cheat sheet
  • @name 会链接到用户页面,并会通知他
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
Ctrl+Enter