iOS开发中如何调试更高效:LLDB与Chisel

·  阅读 1541

lldb是一个使用LLVM基础架构构建的新一代高性能调试器。 lldb会将调试信息转换为clang类型,以便它可以利用clang编译器的基础架构。 即lldb在其表达式中支持C/C++/OC语言的功能和运行时,而无需重新实现这些功能。 还利用编译器来处理函数调用表达式时的所有ABI接口、反汇编指令,提取指令细节等流程,把llvm和clang的基础架构运用到了极致。 可声明局部变量和类型的多行表达式。 需要时可以直接使用clang的JIT表达式。 当JIT不可用时,会直接对中间表示IR进行还原。

GDB to LLDB command map

LLDB

LLDB命令的简写规则: 如果一种写法仅能唯一对应一种全名的命令, 则可以用作简写.

p与po

注意,这里并不是print和print object。而是 expression -- 和 ***expression --object-description --***。

po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
po [[UIWindow keyWindow] recursiveDescription]
po [$myButton allTargets] # 查看按钮事件的接收者.
p self.view
p $R11
po (Class)0x00000002821d6a00
复制代码

常见的po,其实跟e -O --是一样的.

以对象的方式打印结果

e -O -- myArray
复制代码

p的打印格式

p 16    # 默认格式
p/x 16  # 16进制, 0x10
p/t 16  # 二进制
p/c 打印字符
复制代码

expression

expr char *$str = (char *)malloc(8)
expr (void)strcpy($str, "munkeys")
expr $str[1] = 'o'
expr (void)free($str)
复制代码

使用expr可以动态调试:

expr id $myView = (id)0x7f82b1d01fd0
expr (void)[$myView setBackgroundColor:[UIColor blueColor]]
expr (void)[CATransaction flush]
e $R11!.backgroundColor = UIColor.green
e CATransaction.flush()
复制代码

通常情况下, 界面上的变化, 必须在程序继续运行才能看到. 因为变化必须发送到渲染服务(backboardd)中.

而CATransaction的flush操作, 即可做到不继续运行也能实时更新界面. chisel中的caflush即如此.

动态操作UIViewController

e id $nvc = [[[UIApplication sharedApplication] keyWindow] rootViewController]
e id $vc = [UIViewController new]
e (void)[[$vc view] setBackgroundColor:[UIColor yellowColor]]
e (void)[$vc setTitle:@"Yay!"]
e (void)[$nvc pushViewContoller:$vc animated:YES]
caflush // e (void)[CATransaction flush]
复制代码

查看栈信息

bt
bt all
复制代码

断点

br list
br dis 1 # disable一个断点
br del 1 # 删除一个断点
br s -a 0x0023234f
br set -f main.m -l 16 # 新建一个断点
breakpoint set -F "-[NSArray objectAtIndex:]"
br s -a 0x02107730+0x000ab000 -c '(BOOL)[(NSString *)$r2 isEqualToString:@"snakeninny"]'
b -[NSResponder mouseUp:]
复制代码

观察变量

watchpoint set v weakstr
复制代码

dis

反汇编,可以用来查看 NSMallocBlock 的信息.

dis -s *(void**)(0x600002f51110+16)
复制代码

dis -s [内存地址] 该命令可用于反汇编某个地址对应的符号信息及开始一部分的汇编实现.

因block对象的内部偏移16 bytes的位置就是block对象所保存的执行代码的函数地址.

block的内部实现:

struct __block_impl {
    void *isa;  // 标记属于哪种block, 一共有三种. 8 bytes
    int Flags;  // 4 bytes
    int Reserved; // 4 bytes
    void *FuncPtr; // 所以, block对应的执行代码的偏移量即为16 bytes
}
复制代码

查看内存x

memory read的格式:

memory_read/[数量][格式][字节数] 内存地址
复制代码

简写:

x/[数量][格式][字节数] 内存地址
复制代码

其中,

  • 格式: x是16进制, f是浮点数, d是10进制.
  • 字节大小: b是byte; h是half word(两字节); w是word(4字节); g是giant word(8字节)
  • x/4xw 0x123456789: 表示按照16进制, 每次一个word读取, 共读取4次.

(lldb) x/16xg 0x28118ed80
0x28118ed80: 0x000025a105bb2255 0x00000002811a3d80
0x28118ed90: 0x0000000282eda5e0 0x00000002821d6a00
0x28118eda0: 0x00000002831816c0 0x00000002805c6c10
0x28118edb0: 0x00000002805e4d50 0x0000000000000000
0x28118edc0: 0x000000028118ee00 0x000000028118f180
0x28118edd0: 0x0000000280ba8e88 0x0000000000000001
0x28118ede0: 0x0000000148431202 0x0000000000000001
0x28118edf0: 0x00000002808c8730 0x0000000000000000
复制代码

根据表达式计算

x/1w `$str + 3` # 去掉str字符串的前三个字节.
复制代码

process

process plugin packet send qGetProfileData
复制代码
(lldb) process plugin packet send qGetProfileData
  packet: qGetProfileData
response: num_cpu:2;host_user_ticks:53835219;host_sys_ticks:0;host_idle_ticks:304497309;elapsed_usec:1592816260139518;task_used_usec:11353;thread_used_id:7031e7;thread_used_usec:693129;thread_used_name:;thread_used_id:70326f;thread_used_usec:2256;thread_used_name:;thread_used_id:703272;thread_used_usec:4102;thread_used_name:636f6d2e6170706c652e75696b69742e6576656e7466657463682d746872656164;thread_used_id:703273;thread_used_usec:5171;thread_used_name:;thread_used_id:703274;thread_used_usec:1050;thread_used_name:;thread_used_id:703285;thread_used_usec:12;thread_used_name:;total:2099249152;used:1428979712;anonymous:13664256;phys_footprint:13959952;mem_cap:1450;cpu_cap_p:50;cpu_cap_t:180;--end--;
复制代码

查看跟 Profile 工具一致的信息。即Xcode是通过 std::string MachTask::GetProfileData(DNBProfileDataScanType scanType) 来获取App的各类信息的。如:

static void GetPurgeableAndAnonymous(task_t task, uint64_t &purgeable,
                                     uint64_t &anonymous) {
#if defined(TASK_VM_INFO) && TASK_VM_INFO >= 22

  kern_return_t kr;
  mach_msg_type_number_t info_count;
  task_vm_info_data_t vm_info;

  info_count = TASK_VM_INFO_COUNT;
  kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info,
                 &info_count);
  if (kr == KERN_SUCCESS) {
    purgeable = vm_info.purgeable_volatile_resident;
    anonymous =
        vm_info.internal + vm_info.compressed - vm_info.purgeable_volatile_pmap;
  }

#endif
}
复制代码
phys_footprint = (internal - alternate_accounting) + (internal_compressed - alternate_accounting_compressed) + iokit_mapped + purgeable_nonvolatile + purgeable_nonvolatile_compressed + page_table
复制代码

GetProfileData 函数会在 RNBRemote 接收到消息包 qGetProfileData 时执行,而LLDB既可发送这一类消息包。

其他有用的用法

frame

frame info可以查看当前行数和源码文件.

控制程序流程

thread return NO 可以使得当前函数立即返回伪造的返回值.

观察实例变量的变化

查看UIView的_layer何时被重写了.

# 找到_layer变量在对象上的相对位置
p (ptrdiff_t)ivar_getOffset((struct Ivar *)class_getInstanceVariable([MyView class], "_layer"))
# 即($myView + 8)即为被写入的内存地址
watchpoint set expression -- (int *)($myView + 8)
复制代码

chisel中的wivar $myView _layer即如此.

列举用到的库

image list -o -f
复制代码

查看对象引用计数

CFGetRetainCount((__bridge CFTypeDef)obj)
[obj retainCount]
复制代码

真机模拟慢速动画

expr -- [[UIApplication sharedApplication] keyWindow].layer.speed = 0.1 
复制代码

image命令组合

寻找栈地址对应的代码位置.

image lookup --address 0x0000000106d71a12
复制代码

不明类型或类型不匹配的问题

p NSLog(@"%@",[self.view viewWithTag:1001]) 
# error: 'NSLog' has unknown return type; cast the call to its declared return type 
# error: 1 errors parsing expression
复制代码

如果在使用LLDB命令中发现有 unknown type 的类似错误(多见于id类型,比如NSArray中某个值), 那我们就必须显式声明类型。比如上面这个命令,我们得这么修改。

p (void)NSLog(@"%@",[self.view  viewWithTag:1001])
复制代码

如 po self.view.frame 改用 (CGRect)[self.view frame]

chisel

chisel不能使用的问题

error: module importing failed: Missing parentheses in call to 'print'. 
Did you mean print('Whoops! You are missing the <' + arg.argName + '> argument.')? (fblldb.py, line 98)
复制代码

解决办法:

Xcode不支持Python 2.x,所以,使用最新的chisel,替换brew安装目录/usr/local/Cellar/chisel/1.8.1/libexec中的文件即可。
复制代码

常见命令

命令 用法 作用
pviews pviews 查看View的层级
pvc pvc 查看VC的层级
presponder presponder 0x7fce735209e0 获取响应连
pclass pclass 0x7fce735209e0 查看类的继承关系
pmethods pmethods 0x7fce735209e0 查看类的所有方法
pinternals pinternals 0x7fce735209e0 查看类的成员变量
fvc,fv fvc RootViewController 获取类的实例列表
taplog taplog 断点一次可处理响应事件的View,
而不用找到对应代码再打断点
flicker flicker 0x7ffc03438290 使得当前View闪一次
vs vs 0x7ffc03438290 查看图层的当前层级,上一个层级,下一个层级
visualize visualize 0x7fce735209e0 预览图片,颜色,View等
pactions pactions 0x7fce735209e0 查看button所在界面和点击事件
pblock pblock 0x1140c7060 查看block
show,hide show 0x7fce735209e0 显示,隐藏View
mask,unmask mask 0x7fce735209e0 找出view在哪里
border/unborder border 0x7fce735209e0 找出view在哪里
caflush caflush 强制核心动画刷新, 重绘UI界面.
Flush the render server (equivalent to a "repaint" if no animations are in-flight).
bmessage bmessage -[MyViewController viewWillAppear:] 添加符号断点到一个方法上,不用担心哪个类具体实现了该方法。
Set a symbolic breakpoint on the method of a class or the method of an instance
without worrying which class in the hierarchy actually implements the method.
会在其父类的 viewWillAppear: 方法中打断点,并添加上了条件:[self isKindOfClass:[MyViewController class]]
wivar wivar $myView _layer Set a watchpoint on an instance variable of an object

自定义命令

流程:

.lldbinit -> 加载py文件 -> 执行__lldb_init_module -> 完成自定义命令添加
复制代码

建立~/.lldbinit文件

command script import /usr/local/Cellar/chisel/1.8.1/libexec/fblldb.py
复制代码

编写命令

#!/usr/bin/python
# Example file with custom commands, located at /magical/commands/example.py

import lldb
import fblldbbase as fb

def lldbcommands():
  return [ PrintKeyWindowLevel() ]
  
class PrintKeyWindowLevel(fb.FBCommand):
  def name(self):
    return 'pkeywinlevel'
    
  def description(self):
    return 'An incredibly contrived command that prints the window level of the key window.'
    
  def run(self, arguments, options):
    # It's a good habit to explicitly cast the type of all return
    # values and arguments. LLDB can't always find them on its own.
    # lldb.debugger.HandleCommand('p (CGFloat)[(id)[(id)[UIApplication sharedApplication] keyWindow] windowLevel]')
    lldb.debugger.HandleCommand('po UIApplication.shared.keyWindow?.rootViewController!')
复制代码

将命令导入chisel

修改~/.lldbinit文件

command script import /usr/local/Cellar/chisel/1.8.1/libexec/fblldb.py
script fblldb.loadCommandsInDirectory('/usr/local/Cellar/chisel/1.8.1/libexec/commands/')
复制代码

Development Workflow

Developing commands, whether for local use or contributing to Chisel directly, both follow the same workflow. 
Create a command as described in the Custom Commands section and then

Start LLDB
Reach a breakpoint (or simply pause execution via the pause button in Xcode's debug bar or process interrupt if attached directly)
Execute command source ~/.lldbinit in LLDB to source the commands
Run the command you are working on
Modify the command
Optionally run script reload(modulename)
Repeat steps 3-6 until the command becomes a source of happiness
复制代码

关键: 在lldb中执行command source ~/.lldbinit即可动态加载命令。

所有LLDB命令

(lldb) help
Debugger commands:
  apropos           -- List debugger commands related to a word or subject.
  breakpoint        -- Commands for operating on breakpoints (see 'help b' for
                       shorthand.)
  bugreport         -- Commands for creating domain-specific bug reports.
  command           -- Commands for managing custom LLDB commands.
  disassemble       -- Disassemble specified instructions in the current
                       target.  Defaults to the current function for the
                       current thread and stack frame.
  expression        -- Evaluate an expression on the current thread.  Displays
                       any returned value with LLDB's default formatting.
  frame             -- Commands for selecting and examing the current thread's
                       stack frames.
  gdb-remote        -- Connect to a process via remote GDB server.  If no host
                       is specifed, localhost is assumed.
  gui               -- Switch into the curses based GUI mode.
  help              -- Show a list of all debugger commands, or give details
                       about a specific command.
  kdp-remote        -- Connect to a process via remote KDP server.  If no UDP
                       port is specified, port 41139 is assumed.
  language          -- Commands specific to a source language.
  log               -- Commands controlling LLDB internal logging.
  memory            -- Commands for operating on memory in the current target
                       process.
  platform          -- Commands to manage and create platforms.
  plugin            -- Commands for managing LLDB plugins.
  process           -- Commands for interacting with processes on the current
                       platform.
  quit              -- Quit the LLDB debugger.
  register          -- Commands to access registers for the current thread and
                       stack frame.
  reproducer        -- Commands controlling LLDB reproducers.
  script            -- Invoke the script interpreter with provided code and
                       display any results.  Start the interactive interpreter
                       if no code is supplied.
  settings          -- Commands for managing LLDB settings.
  source            -- Commands for examining source code described by debug
                       information for the current target process.
  statistics        -- Print statistics about a debugging session
  target            -- Commands for operating on debugger targets.
  thread            -- Commands for operating on one or more threads in the
                       current process.
  type              -- Commands for operating on the type system.
  version           -- Show the LLDB debugger version.
  watchpoint        -- Commands for operating on watchpoints.
Current command abbreviations (type 'help command alias' for more info):
  add-dsym  -- Add a debug symbol file to one of the target's current modules
               by specifying a path to a debug symbols file, or using the
               options to specify a module to download symbols for.
  attach    -- Attach to process by ID or name.
  b         -- Set a breakpoint using one of several shorthand formats.
  bt        -- Show the current thread's call stack.  Any numeric argument
               displays at most that many frames.  The argument 'all' displays
               all threads.
  c         -- Continue execution of all threads in the current process.
  call      -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  continue  -- Continue execution of all threads in the current process.
  detach    -- Detach from the current target process.
  di        -- Disassemble specified instructions in the current target. 
               Defaults to the current function for the current thread and
               stack frame.
  dis       -- Disassemble specified instructions in the current target. 
               Defaults to the current function for the current thread and
               stack frame.
  display   -- Evaluate an expression at every stop (see 'help target
               stop-hook'.)
  down      -- Select a newer stack frame.  Defaults to moving one frame, a
               numeric argument can specify an arbitrary number.
  env       -- Shorthand for viewing and setting environment variables.
  exit      -- Quit the LLDB debugger.
  f         -- Select the current stack frame by index from within the current
               thread (see 'thread backtrace'.)
  file      -- Create a target using the argument as the main executable.
  finish    -- Finish executing the current stack frame and stop after
               returning.  Defaults to current thread unless specified.
  image     -- Commands for accessing information for one or more target
               modules.
  j         -- Set the program counter to a new address.
  jump      -- Set the program counter to a new address.
  kill      -- Terminate the current target process.
  l         -- List relevant source code using one of several shorthand formats.
  list      -- List relevant source code using one of several shorthand formats.
  n         -- Source level single step, stepping over calls.  Defaults to
               current thread unless specified.
  next      -- Source level single step, stepping over calls.  Defaults to
               current thread unless specified.
  nexti     -- Instruction level single step, stepping over calls.  Defaults to
               current thread unless specified.
  ni        -- Instruction level single step, stepping over calls.  Defaults to
               current thread unless specified.
  p         -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  parray    -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  po        -- Evaluate an expression on the current thread.  Displays any
               returned value with formatting controlled by the type's author.
  poarray   -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  print     -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  q         -- Quit the LLDB debugger.
  r         -- Launch the executable in the debugger.
  rbreak    -- Sets a breakpoint or set of breakpoints in the executable.
  re        -- Commands to access registers for the current thread and stack
               frame.
  repl      -- Evaluate an expression on the current thread.  Displays any
               returned value with LLDB's default formatting.
  run       -- Launch the executable in the debugger.
  s         -- Source level single step, stepping into calls.  Defaults to
               current thread unless specified.
  si        -- Instruction level single step, stepping into calls.  Defaults to
               current thread unless specified.
  sif       -- Step through the current block, stopping if you step directly
               into a function whose name matches the TargetFunctionName.
  step      -- Source level single step, stepping into calls.  Defaults to
               current thread unless specified.
  stepi     -- Instruction level single step, stepping into calls.  Defaults to
               current thread unless specified.
  t         -- Change the currently selected thread.
  tbreak    -- Set a one-shot breakpoint using one of several shorthand formats.
  undisplay -- Stop displaying expression at every stop (specified by stop-hook
               index.)
  up        -- Select an older stack frame.  Defaults to moving one frame, a
               numeric argument can specify an arbitrary number.
  v         -- Show variables for the current stack frame. Defaults to all
               arguments and local variables in scope. Names of argument,
               local, file static and file global variables can be specified.
               Children of aggregate variables can be specified such as
               'var->child.x'.  The -> and [] operators in 'frame variable' do
               not invoke operator overloads if they exist, but directly access
               the specified element.  If you want to trigger operator
               overloads use the expression command to print the variable
               instead.
               It is worth noting that except for overloaded operators, when
               printing local variables 'expr local_var' and 'frame var
               local_var' produce the same results.  However, 'frame variable'
               is more efficient, since it uses debug information and memory
               reads directly, rather than parsing and evaluating an
               expression, which may even involve JITing and running code in
               the target program.
  var       -- Show variables for the current stack frame. Defaults to all
               arguments and local variables in scope. Names of argument,
               local, file static and file global variables can be specified.
               Children of aggregate variables can be specified such as
               'var->child.x'.  The -> and [] operators in 'frame variable' do
               not invoke operator overloads if they exist, but directly access
               the specified element.  If you want to trigger operator
               overloads use the expression command to print the variable
               instead.
               It is worth noting that except for overloaded operators, when
               printing local variables 'expr local_var' and 'frame var
               local_var' produce the same results.  However, 'frame variable'
               is more efficient, since it uses debug information and memory
               reads directly, rather than parsing and evaluating an
               expression, which may even involve JITing and running code in
               the target program.
  vo        -- Show variables for the current stack frame. Defaults to all
               arguments and local variables in scope. Names of argument,
               local, file static and file global variables can be specified.
               Children of aggregate variables can be specified such as
               'var->child.x'.  The -> and [] operators in 'frame variable' do
               not invoke operator overloads if they exist, but directly access
               the specified element.  If you want to trigger operator
               overloads use the expression command to print the variable
               instead.
               It is worth noting that except for overloaded operators, when
               printing local variables 'expr local_var' and 'frame var
               local_var' produce the same results.  However, 'frame variable'
               is more efficient, since it uses debug information and memory
               reads directly, rather than parsing and evaluating an
               expression, which may even involve JITing and running code in
               the target program.
  x         -- Read from the memory of the current target process.
Current user-defined commands:
  alamborder    -- Put a border around views with an ambiguous layout
  alamunborder  -- Removes the border around views with an ambiguous layout
  bdisable      -- Disable a set of breakpoints for a regular expression
  benable       -- Enable a set of breakpoints for a regular expression
  binside       -- Set a breakpoint for a relative address within the
                   framework/library that's currently running. This does the
                   work of finding the offset for the framework/library and
                   sliding your address accordingly.
  bmessage      -- Set a breakpoint for a selector on a class, even if the
                   class itself doesn't override that selector. It walks the
                   hierarchy until it finds a class that does implement the
                   selector and sets a conditional breakpoint there.
  border        -- Draws a border around <viewOrLayer>. Color and width can be
                   optionally provided. Additionally depth can be provided in
                   order to recursively border subviews.
  caflush       -- Force Core Animation to flush. This will 'repaint' the UI
                   but also may mess with ongoing animations.
  dcomponents   -- Set debugging options for components.
  dismiss       -- Dismiss a presented view controller.
  fa11y         -- Find the views whose accessibility labels match labelRegex
                   and puts the address of the first result on the clipboard.
  findinstances -- Find instances of specified ObjC classes.
  flicker       -- Quickly show and hide a view to quickly help visualize where
                   it is.
  fv            -- Find the views whose class names match classNameRegex and
                   puts the address of first on the clipboard.
  fvc           -- Find the view controllers whose class names match
                   classNameRegex and puts the address of first on the
                   clipboard.
  heapfrom      -- Show all nested heap pointers contained within a given
                   variable.
  hide          -- Hide a view or layer.
  mask          -- Add a transparent rectangle to the window to reveal a
                   possibly obscured or hidden view or layer's bounds
  mwarning      -- simulate a memory warning 模拟一个内存警告
  pa11y         -- Print accessibility labels of all views in hierarchy of
                   <aView>
  pa11yi        -- Print accessibility identifiers of all views in hierarchy of
                   <aView>
  pactions      -- Print the actions and targets of a control.
  paltrace      -- Print the Auto Layout trace for the given view. Defaults to
                   the key window.
  panim         -- Prints if the code is currently execution with a UIView
                   animation block.
  pbcopy        -- Print object and copy output to clipboard
  pblock        -- Print the block`s implementation address and signature
  pbundlepath   -- Print application's bundle directory path. 打印应用bundle目录地址
  pcells        -- Print the visible cells of the highest table view in the
                   hierarchy. 打印最高层的tableview的可见cell
  pclass        -- Print the inheritance starting from an instance of any class.
  pcomponents   -- Print a recursive description of components found starting
                   from <aView>.
  pcurl         -- Print the NSURLRequest (HTTP) as curl command.
  pdata         -- Print the contents of NSData object as string.
  pdocspath     -- Print application's 'Documents' directory path. 打印应用的Documents目录路径
  pinternals    -- Show the internals of an object by dereferencing it as a
                   pointer.
  pinvocation   -- Print the stack frame, receiver, and arguments of the
                   current invocation. It will fail to print all arguments if
                   any arguments are variadic (varargs).
  pivar         -- Print the value of an object's named instance variable.
  pjson         -- Print JSON representation of NSDictionary or NSArray object 使用json格式打印字典或数组
  pkp           -- Print out the value of the key path expression using
                   -valueForKeyPath:
  pmethods      -- Print the class and instance methods of a class.
  poobjc        -- Print the expression result, with the expression run in an
                   ObjC++ context. (Shortcut for "expression -O -l ObjC++ -- " )
  pproperties   -- Print the properties of an instance or Class
  present       -- Present a view controller.
  presponder    -- Print the responder chain starting from a specific responder.
  psjson        -- Print JSON representation of Swift Dictionary or Swift Array
                   object
  ptv           -- Print the highest table view in the hierarchy.
  pvc           -- Print the recursion description of <aViewController>.
  pviews        -- Print the recursion description of <aView>.
  rcomponents   -- Synchronously reflow and update all components.
  sequence      -- Run commands in sequence, stopping on any error.
  setinput      -- Input text into text field or text view that is first
                   responder.
  settext       -- Set text on text on a view by accessibility id.
  show          -- Show a view or layer.
  slowanim      -- Slows down animations. Works on the iOS Simulator and a
                   device.
  taplog        -- Log tapped view to the console.
  uikit         -- Imports the UIKit module to get access to the types while in
                   lldb.
  unborder      -- Removes border around <viewOrLayer>.
  unmask        -- Remove mask from a view or layer
  unslowanim    -- Turn off slow animations.
  visualize     -- Open a UIImage, CGImageRef, UIView, or CALayer in
                   Preview.app on your Mac.
  vs            -- Interactively search for a view by walking the hierarchy.
  wivar         -- Set a watchpoint for an object's instance variable.
  xdebug        -- Print debug description the XCUIElement in human readable
                   format.
  xnoid         -- Print XCUIElement objects with label but without identifier.
  xobject       -- Print XCUIElement details.
  xtree         -- Print XCUIElement subtree.
  zzz           -- Executes specified lldb command after delay.
For more information on any command, type 'help <command-name>'.
复制代码

参考资料

分类:
开发工具
标签:
分类:
开发工具
标签:
收藏成功!
已添加到「」, 点击更改