错误预防
断言:
- 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变化:
wiar1
(lldb) wivar $myView _layer
在message上设置断点:
bmessage1
(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/)