Debug笔记

错误预防

断言:

  • NSParameterAssert/NSAssert用于ObjC函数,NSCParameterAssert/NSCAssert用于C函数
  • ObjC函数中NSParameterAssert用在开头检查输入参数,其他情况都用NSAssert
  • 不用assert,因为它只终止程序不输出信息

参数非nil的编译检查:

1
2
3
- (void)doSomethingWithRequiredString:(NSString *)requiredString 
bar:(NSString *)optionalString
__attribute((nonnull(1)));

LLDB

e(即expression)对表达式求值,是lldb最基本的命令

p(即print)是expression --的简写,poexpression -O --的简写

p/<format>可以指定print的输出格式,如:

1
2
(lldb) p/x 16            // 0x10
(lldb) p/t (char)16 // 0b00010000,t stands for two

lldb可以声明变量名以$开始的变量:

1
2
(lldb) e int $a = 2
(lldb) p $a * 19 // 38
1
2
(lldb) e NSArray *$array = @[ @"Sunday", @"Monday" ]
(lldb) p [$array count] // 2

若lldb不清楚返回值的类型,需要明确指定:

1
(lldb) p (char)[[$array objectAtIndex:0] characterAtIndex:0]    // 'S'

或者导入框架,然后就不需指定返回值类型:

1
2
(lldb) expr @import UIKit
(lldb) p slef.window.bounds

为方便起见,可在~/.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
2
3
4
5
6
7
8
(lldb) po [$myButton allTargets]
{(
<MagicEventListener: 0x7fb58bd2e240>
)}
(lldb) po [$myButton actionsForTarget:(id)0x7fb58bd2e240 forControlEvent:0]
<__NSArrayM 0x7fb58bd2aa40>(
_handleTap:
)

然后在-[MyEventListener _handleTap:]上设置符号断点

Chisel

LLDB内置支持python,通过script命令可调用python代码:

1
2
(lldb) script import os
(lldb) script os.system("open http://www.objc.io/")

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
2
3
[UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated) {
NSLog(@"View Controller %@ will appear animated: %tu", aspectInfo.instance, animated);
} error:NULL];

其中block的第一个参数是id<AspectInfo>,其他参数(可选)就是被hook方法的参数列表

参考: