V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jpush
V2EX  ›  iOS

iOS 调试篇

  •  
  •   jpush · 2017-03-10 13:42:46 +08:00 · 2426 次点击
    这是一个创建于 2845 天前的主题,其中的信息可能已经有所发展或是发生改变。

    修复 bug 占用我们日常开发的大部分时间,熟练的使用调试工具可以给我们节约大部分的时间。

    LLDB 的常用命令

    expression

    expresion 是一个非常常用的命令,我们可以通过这个命令来执行一些表达式,这样我们就不需要重写运行工程了,例如:

      (lldb) expression -- self.view.backgroundColor = [UIColor greenColor]
      (lldb) expression -- (void)[CATransaction flush] // 用于刷新页面,
    

    我们也可以使用 expression 来输出我们关注的信息

    (lldb) expression -O -- self.view //这时候 就会输出对象 UIView ,注意 -O 代表输出的是一个对象 ,这里有另外一种简写方式 po.
    <UIView: 0x7d2974a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7d294813>>
    

    有时候我们自定义的类并没有重写 description 方法,如果我们直接输出这个对象的话可能只会显示这个类名和地址,例如

    (lldb) expression -O -- self
    <JChatAboutMeViewController: 0x7d2974a0>
    

    这地址并不是我们想要的,我们想要的是这个对象内部信息,这里我推荐一个插件 chisel ,我们可以使用 chisel 提供的命令来打印这个对象, 例如

    (lldb) pinternals 0x7d2974a0
    (JChatAboutMeViewController) $4 = {
      UIViewController = {
        UIResponder = {
          NSObject = {
            isa = JChatAboutMeViewController
          }
          _hasOverrideClient = '\0'
          _hasOverrideHost = '\0'
          _hasInputAssistantItem = '\0'
        }
        _overrideTransitioningDelegate = nil
        _view = nil
        _tabBarItem = nil
        _navigationItem = 0x7d4abf40
        _toolbarItems = nil
        _title = nil
        _nibName = nil
        _nibBundle = nil
        _parentViewController = 0x7cb84c00
        _childModalViewController = nil
        _parentModalViewController = nil
        _previousRootViewController = nil
        _modalTransitionView = nil
        _modalPreservedFirstResponder = nil
        _dimmingView = nil
        _dropShadowView = nil
        _currentAction = nil
        _storyboard = nil
        _externalObjectsTableForViewLoading = nil
        _topLevelObjectsToKeepAliveFromStoryboard = nil
        _savedHeaderSuperview = nil
        _savedFooterSuperview = nil
        _editButtonItem = nil
        _searchDisplayController = nil
        _strongSearchDisplayController = nil
        _modalTransitionStyle = 0
        _modalPresentationStyle = 0
        _lastKnownInterfaceOrientation = 0
        _popoverController = nil
        _containerViewInSheet = nil
        _recordedContentScrollView = nil
        _afterAppearance = nil
        _explicitAppearanceTransitionLevel = 0
        _interfaceBuilderKeyCommands = nil
        _addedKeyCommands = nil
        _overrideTraitCollections = nil
        _previewSourceViews = nil
        _retainCount = 0
        _ignoreAppSupportedOrientations = '\0'
        _viewHostsLayoutEngine = '\0'
        _storyboardIdentifier = nil
        _transitioningDelegate = nil
        _frozenTraitCollection = nil
        overrideUseCustomPresentation = '\0'
        _modalPresentationCapturesStatusBarAppearance = '\0'
        _disablesAutomaticKeyboardDismissal = '\0'
        _ignoresParentMargins = '\0'
        _childViewControllers = nil
        _customNavigationInteractiveTransitionDuration = 0
        _customNavigationInteractiveTransitionPercentComplete = 0
        _customTransitioningView = nil
        _lastNotifiedTraitCollection = nil
        _presentationController = nil
        _preferredFocusedItem = nil
        _navigationControllerContentOffsetAdjustment = 0
        _contentMargin = 16
        _topLayoutGuide = nil
        _bottomLayoutGuide = nil
        _topBarInsetGuideConstraint = nil
        _bottomBarInsetGuideConstraint = nil
        _storyboardSegueTemplates = nil
        _segueResponsibleForModalPresentation = nil
        _sourceViewControllerIfPresentedViaPopoverSegue = nil
        _modalSourceViewController = nil
        _presentedStatusBarViewController = nil
        _edgesForExtendedLayout = 15
        __childControllerToIgnoreWhileLookingForTransitionCoordinator = nil
        _presentingFocusedItem = nil
        _storyboardPreviewSegueTemplates = nil
        _storyboardCommitSegueTemplates = nil
        _storyboardPreviewingRegistrants = nil
        __embeddedView = 0xffffffff
        __embeddingView = 0x78b909c0
        __embeddedDelegate = 0x00007faa
        _originalPresentationController = 0x78af3630
        _temporaryPresentationController = 0x00007faa
      }
    }
    

    这样我们就能看到这个对象的内部信息了。 chisel 还提供一些便捷的功能,比如打印 pviews 递归打印层级,不过我更喜欢使用 xcode 自带的 debug view hierarchy ,这样更加直观。

    我们可以使用 pvc 递归输出试图控制器的层级关系,例如

    (lldb) pvc
    <JChatSwift.JChatMainTabViewController 0x7d49d560>, state: appeared, view: <UILayoutContainerView 0x7d479860>
       | <UINavigationController 0x7c3afc00>, state: appeared, view: <UILayoutContainerView 0x7be8e9a0>
       |    | <JChatConversationListViewController 0x7d49d2b0>, state: disappeared, view: <UIView 0x7d375320> not in the window
       |    | <JChatChattingViewController 0x7bfbdde0>, state: appeared, view: <UIView 0x7d373b30>
       | <UINavigationController 0x7eb7a000>, state: disappeared, view: <UILayoutContainerView 0x7d4a2c20> not in the window
       |    | <JChatContactsViewController 0x7bea3d70>, state: disappeared, view: <UILayoutContainerView 0x7d4a1d40> not in the window
       | <UINavigationController 0x7cb84c00>, state: disappeared, view: <UILayoutContainerView 0x7bf87590> not in the window
       |    | <JChatAboutMeViewController 0x7d2974a0>, state: disappeared, view:  (view not loaded)
    

    通过 pvc 和 pinternals 这样我们就可以在任何地方了解我们所有界面状态了。 在 xcode8 以后,我们也可以通过 debug memory graph 来查看程序运行的内存状态。

    thread

    我们可以使用 thread backtrace 来输出线程的堆栈信息,例如

    (lldb) thread backtrace  // 这个命令可以简写为 bt
    * thread #1: tid = 0x14169a, 0x001273e5 JChatSwift`JChatChattingViewController.viewDidLayoutSubviews(self=0x7b6bc560) -> () + 21 at JChatChattingViewController.swift:48, queue = 'com.apple.main-thread', stop reason = breakpoint 7.1
      * frame #0: 0x001273e5 JChatSwift`JChatChattingViewController.viewDidLayoutSubviews(self=0x7b6bc560) -> () + 21 at JChatChattingViewController.swift:48
        frame #1: 0x00127502 JChatSwift`@objc JChatChattingViewController.viewDidLayoutSubviews() -> () + 34 at JChatChattingViewController.swift:0
        frame #2: 0x039dc811 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1598
        frame #3: 0x0305c1b9 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 59
        frame #4: 0x03791769 QuartzCore`-[CALayer layoutSublayers] + 141
        frame #5: 0x03784a47 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 401
        frame #6: 0x0378489d QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 21
        frame #7: 0x0370e49f QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 339
        frame #8: 0x0373d290 QuartzCore`CA::Transaction::commit() + 498
        frame #9: 0x0373eda0 QuartzCore`CA::Transaction::flush_transaction() + 38
        frame #10: 0x0393685c UIKit`_afterCACommitHandler + 375
        frame #11: 0x022f676e CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
        frame #12: 0x022f66c7 CoreFoundation`__CFRunLoopDoObservers + 391
        frame #13: 0x022da3a6 CoreFoundation`__CFRunLoopRun + 1414
        frame #14: 0x022d9bab CoreFoundation`CFRunLoopRunSpecific + 395
        frame #15: 0x022d9a0b CoreFoundation`CFRunLoopRunInMode + 123
        frame #16: 0x06a1ab4c GraphicsServices`GSEventRunModal + 177
        frame #17: 0x06a1a9c7 GraphicsServices`GSEventRun + 80
        frame #18: 0x039077fb UIKit`UIApplicationMain + 148
        frame #19: 0x001a7ce1 JChatSwift`main + 145 at AppDelegate.swift:15
        frame #20: 0x06037799 libdyld.dylib`start + 1
    

    我们可以看到程序停在 JChatChattingViewController.swift:48 这一行

    watchpoint

    监视某个变量的改变,有时候我们想知道一个对象在什么时候被修改了,我们可以使用 watchpoint set var,当 var 改变的时候程序就就会停在改变的地方, 如果用 chisel 的话,我们可以使用 wivar 命令来监听值的变化,例如

    (lldb) wivar self name
    

    作者: HuminiOS - 极光

    原文:iOS 调试篇

    知乎专栏:极光日报


    利益相关: jpush 是极光团队的账号,作者也是团队成员之一,所有帖子都有原创版权而非全文转载,在本账号首页已经清楚注明了。文末的简书链接是团队的文章库,看在上帝的份上,我都不知道被怼甚至被举报了多少次, so sad )

    3 条回复    2017-03-11 11:55:04 +08:00
    chmlai
        1
    chmlai  
       2017-03-10 13:57:28 +08:00   ❤️ 1
    mogutouer
        2
    mogutouer  
       2017-03-11 10:24:39 +08:00
    请问一下,

    比如
    `frame #3: 0x0305c1b9 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 59`

    这最后加号后面的数字是什么意思,好像不是所在第几行
    ngn999
        3
    ngn999  
       2017-03-11 11:55:04 +08:00 via iPhone
    @mogutouer 这应该是没有源码,说的是在汇编里的相对函数入口的偏移。 如果是你自己的代码,会提示 xxxx.swift:48,这个 48 就是行号。关于前者可以 f 3 ,然后 disassembly 看看
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1050 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 19:27 · PVG 03:27 · LAX 11:27 · JFK 14:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.