修復(fù)bug 占用我們?nèi)粘i_發(fā)的大部分時(shí)間椅贱,熟練的使用調(diào)試工具可以給我們節(jié)約大部分的時(shí)間毅舆。
LLDB 的常用命令
expression
expresion 是一個(gè)非常常用的命令,我們可以通過這個(gè)命令來執(zhí)行一些表達(dá)式休讳,這樣我們就不需要重寫運(yùn)行工程了,例如:
(lldb) expression -- self.view.backgroundColor = [UIColor greenColor]
(lldb) expression -- (void)[CATransaction flush] // 用于刷新頁面,
我們也可以使用 expression 來輸出我們關(guān)注的信息
(lldb) expression -O -- self.view //這時(shí)候 就會(huì)輸出對(duì)象 UIView芹橡,注意 -O 代表輸出的是一個(gè)對(duì)象 ,這里有另外一種簡(jiǎn)寫方式 po.
<UIView: 0x7d2974a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7d294813>>
有時(shí)候我們自定義的類并沒有重寫description 方法望伦,如果我們直接輸出這個(gè)對(duì)象的話可能只會(huì)顯示這個(gè)類名和地址,例如
(lldb) expression -O -- self
<JChatAboutMeViewController: 0x7d2974a0>
這地址并不是我們想要的林说,我們想要的是這個(gè)對(duì)象內(nèi)部信息煎殷,這里我推薦一個(gè)插件 chisel ,我們可以使用chisel 提供的命令來打印這個(gè)對(duì)象, 例如
(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
}
}
這樣我們就能看到這個(gè)對(duì)象的內(nèi)部信息了。
chisel 還提供一些便捷的功能腿箩,比如打印 pviews 遞歸打印層級(jí)豪直,不過我更喜歡使用 xcode 自帶的debug view hierarchy,這樣更加直觀珠移。
我們可以使用 pvc 遞歸輸出試圖控制器的層級(jí)關(guān)系弓乙,例如
(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 這樣我們就可以在任何地方了解我們所有界面狀態(tài)了。
在 xcode8 以后钧惧,我們也可以通過debug memory graph 來查看程序運(yùn)行的內(nèi)存狀態(tài)暇韧。
thread
我們可以使用 thread backtrace 來輸出線程的堆棧信息,例如
(lldb) thread backtrace // 這個(gè)命令可以簡(jiǎn)寫為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
監(jiān)視某個(gè)變量的改變浓瞪,有時(shí)候我們想知道一個(gè)對(duì)象在什么時(shí)候被修改了懈玻,我們可以使用 watchpoint set var,當(dāng)var 改變的時(shí)候程序就就會(huì)停在改變的地方, 如果用 chisel
的話乾颁,我們可以使用 wivar 命令來監(jiān)聽值的變化,例如
(lldb) wivar self name