错误预防
断言:
- NSParameterAssert/NSAssert用于ObjC函数,NSCParameterAssert/NSCAssert用于C函数
- ObjC函数中
NSParameterAssert
用在开头检查输入参数,其他情况都用NSAssert
- 不用
assert
,因为它只终止程序不输出信息
参数非nil的编译检查:
1 | - (void)doSomethingWithRequiredString:(NSString *)requiredString |
LLDB
e
(即expression
)对表达式求值,是lldb最基本的命令
p
(即print
)是expression --
的简写,po
是expression -O --
的简写
p/<format>
可以指定print的输出格式,如:
1 | (lldb) p/x 16 // 0x10 |
lldb可以声明变量名以$
开始的变量:
1 | (lldb) e int $a = 2 |
1 | (lldb) e NSArray *$array = @[ @"Sunday", @"Monday" ] |
若lldb不清楚返回值的类型,需要明确指定:
1 | (lldb) p (char)[[$array objectAtIndex:0] characterAtIndex:0] // 'S' |
或者导入框架,然后就不需指定返回值类型:
1 | (lldb) expr @import UIKit |
为方便起见,可在~/.lldbinit中添加:
1 | command alias uikit expr @import UIKit |
c
/n
/s
/fin
(即continue / next / step / finish)分别对应Continue、Step Over、Step Into、Step Out
frame info
可以查看当前函数调用帧
thread return [value]
可以让函数直接return或return某个值
例子
查看window的hierarchy(pviews):
1 | po [[UIWindow keyWindow] recursiveDescription] |
查看viewController的hierarchy(pvc):
1 | po [[[UIWindow keyWindow] rootViewController] _printHierarchy] // _printHierarchy是iOS8里的私有方法 |
更改视图属性后刷新界面(caflush):
1 | e (void)[CATransaction flush] |
先找到某button的target:
1 | (lldb) po [$myButton allTargets] |
然后在-[MyEventListener _handleTap:]
上设置符号断点
Chisel
LLDB内置支持python,通过script
命令可调用python代码:
1 | (lldb) script import os |
Chisel就是通过~/.lldbinit
加载的便利iOS调试的python脚本集合:
查看window的hierarchy:
pviews
查看viewController的hierarchy:
pvc
查看responder链:
presponder
更改视图属性后刷新界面:
caflush
显示/隐藏视图:
show
/hide
监视ivar变化:
wiar
1
(lldb) wivar $myView _layer
在message上设置断点:
bmessage
1
(lldb) bmessage -[MyViewController viewDidAppear:]
Aspects
用Aspects可以hook进方法打印log:
1 | [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated) { |
其中block的第一个参数是id<AspectInfo>
,其他参数(可选)就是被hook方法的参数列表
参考:
- Debugging: A Case Study
- Dancing in the Debugger — A Waltz with LLDB
- [An @import-ant Change in Xcode](http://furbo.org/2015/05/11/an-import-ant-change-in-xcode/)