iOs Debug Master

    Once I almost completely abandoned the mouse to navigate Xcode and am quite happy about it. The next step is to abandon the visual controls of the debugger. What for? - We increase opportunities, reduce debug time, spend less calories to move a heavy hand (we need calories to work with our heads) and thereby provoke less tunnel syndrome.


    .
    There will be no particularly new discoveries in the article, I’ll just summarize that you yourself knew well and how this can be used for someone in a new way.

    I do not consider such topics: how to work in Xcode, how to debug the application at all, at what point you need to do this, and why, what is LLDB , what is Step Into and Step Over, etc.

    Go to the examples:

    Customize the console


    We refuse this panel here:



    Yes, and soon we will forget about this



    Now our new best friend is this alluring white canvas, you can stretch it even to the full length or even the whole screen.



    Thanks to my colleague: he fulfilled my old dream - to open the debugger console in a new window. And it’s always annoying that I just opened and set up the tabs as you need, so right away at the first breakpoint everything went wrong. To avoid this, go to Xcode -> Behaviours -> Edit behaviours ... Next, we need the Running -> Pause section .

    Select Show tab named , write our unique tab name there, for example Debug (I note that the tab is not duplicated during repeated launches), at the end we putactive window - this, oddly enough, opens a new tab (Cmd + T) for our debugger. I also hid the unnecessary right panel - but this is optional. In general, customization of the environment under different conditions is in the Behaviours section.

    image

    Everything, we are tuned, we move on.

    Managing the Debugger


    Add directly into the treasury of useful hotkey'ev who did not know Cmd + the C + the Shift . This is a quick transition to the console.
    Cmd + Shift + Y is to hide / show the console.
    Our new teams:
    Step Into - step or short s
    Step Over - next or short n
    Step Out - finish
    Continue - continue or c
    Disable all breakpoints - breakpoint disable

    For a complete list of lldb commands, go here
    I will not list all the features of direct LLDB commands, but their list is larger than the visual environment Xcode allows and everyone has the right to decide whether to use this approach or not and which commands are needed and interesting. I will dwell on what showed where to look.

    The convenience of executing commands is also that there is a transition to the previous / next command through the up and down arrows that is familiar as in a terminal.

    As a result, our work with the debugger looks like this:



    Swift and obj-c! What is inside the object?


    I don’t know about you, but I was not lucky to write a project on Swift from scratch, but I had to write all new files in Swifte with the release of a new language in a huge obj-c project. I don’t write about all the inconsistencies, but the main problem in debugging is probably in the following:

    Create a simple model class on Swift

    class TestObject: NSObject {
        var name: String = "name"
        var index: Int = 123
    }
    

    Now we write in obj-c a simple array with our class:
    NSArray *array = @[[[TestObject alloc] init],
                     [[TestObject alloc] init],
                     [[TestObject alloc] init]];
    

    Now we put a breakpoint on the next line and look at what we would see in Variables:

    image

    Is that familiar? And where are our name and index
    Now we look at what we can do with our hands in the console:

    (lldb) p array
    (__NSArrayI *) $0 = 0x00007f9a1878c6b0 @"3 objects"
    (lldb) po array
    <__NSArrayI 0x7f9a1878c6b0>(
    ,
    ,
    
    )
    (lldb) po [array debugDescription]
    <__NSArrayI 0x7f9a1878c6b0>(
    ,
    ,
    
    )
    (lldb) po array[0]
    
    (lldb) po array[0].name
    error: property 'name' not found on object of type 'id'
    error: 1 errors parsing expression
    (lldb) po [array[0] name]
    name
    (lldb) 
    

    p - this is print
    po - this is a print object , throws a description message to the object, note that the objects can still be sent a debugDescription
    po array [0] .name - it does not work, because for the debugger there is a null element in an array of type id. But sending a message name (po [ array [0] name] ) works fine. Do not forget that obj-c is a message oriented programming language.

    find me


    The next case: we have an API - we go to the server for a list of countries, then we convert them to internal logic and store them somewhere. For example, our modelView goes for data in the store and looks for the model in the dictionary by key:
    - (CACountry *)countryByCode:(NSString *)code
    {
        return  [_countries objectForKey:code];
    }
    

    What do we see in Variables? 242 countries, excellent, how can we quickly find Russia?
    image

    I know, like this:
    (lldb) po [_countries objectForKey:@"RU"]
    
    (lldb) po [[[_countries objectForKey:@"RU"] title] string]
    Россия
    

    And I want to note that autocompletion works fine in the debugger line to send messages to objective-c

    Magical UIView


    Surely, if you were doing some non-trivial UI, there were situations when something in the interface moved out and you need to figure out who is to blame. With this restart, the application threatens that it may not be able to reproduce the situation. This is where working with the debugger is indispensable.
    As a result of doing simple
    (lldb) po self.view.subviews
    

    we'll see something like this:
    ; layer = ; contentOffset: {0, -180}; contentSize: {375, 843}> collection view layout: ,
    >,
    >
    

    Immediately we see a set of views, their frames, properties, everything that Apple developers stuffed into a standard description message.
    If you need, we can easily clarify some property, you can use the address in memory directly:
    (lldb) po [0x7fae2b792ba0 backgroundColor]
    UIDeviceWhiteColorSpace 0 1
    

    But finally, a piece of code that can get all the views of a given class in a hierarchy may seem useful. I use it myself.
    import UIKit
    extension UIView {
        func debugAllSubviewsOfClass(cls: AnyClass) -> [String] {
            func goDeepAndPrint(inout views: [String], currentView: UIView) {
                for v in currentView.debugSubview() {
                    views.append(v.debugDescriptionWithParent())
                    goDeepAndPrint(&views, currentView:v)
                }
            }
            var views = [String]()
            goDeepAndPrint(&views, currentView:self)
            return views
        }
        func debugDescriptionWithParent() -> String {
            let parentAddress = self.superview != nil ? String(format: "%p", self.superview!) : "nil"
            return "\(self.description), parent = \(parentAddress)"
        }
        func debugSubview() -> [UIView] {
            return self.subviews
        }
    }
    extension UITableView {
        override func debugSubview() -> [UIView] {
            return self.subviews + self.visibleCells
        }
    }
    extension UICollectionView {
        override func debugSubview() -> [UIView] {
            return self.subviews + self.visibleCells()
        }
    }
    

    UPD: expanding the LLDB instruction set


    There is a good collection of binded commands for LLDB from Facebook - Chisel
    For example, there is already a command with a recursive output of nested views - pviews and controllers - pvc There are convenient show / hide commands that allow you to hide or show this or that layer without view, continue.
    There is a description of commands and installation instructions in the git, I just want to note that you need a brew package manager if it is not there and create a .lldbinit file with your hands, which you, as mine, may not have and add the code that brew gives after installing chisel'a:
    ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    ...
    brew install chisel
    ==> Downloading https://github.com/facebook/chisel/archive/1.3.0.tar.gz
    ==> Downloading from https://codeload.github.com/facebook/chisel/tar.gz/1.3.0
    ######################################################################## 100.0%
    ==> Caveats
    Add the following line to ~/.lldbinit to load chisel when Xcode launches:
      command script import /usr/local/opt/chisel/libexec/fblldb.py
    

    Then create the .lldbinit file in the root directory and put the script in there:
    command script import /usr/local/opt/chisel/libexec/fblldb.py
    

    Please note that the script may change, for example, the one indicated in the chisel’s own git is different.
    Commands will be available when restarting Xcode, if you are too lazy to do this, you can:
    command source ~/.lldbinit
    

    Summary


    • You can work with the debugger not only through the Xcode buttons.
    • How to configure console opening when debugging in a new tab? The very beginning - the big picture
    • Fast transition to the console - Cmd + Shift + C ,
      minimize / expand the console - Cmd + Shift + Y
    • Our new team:
      the Step Into - step or short s
      the Step Over - next or short n
      the Continue - 'continue' or c
      Disable all breakpoints - breakpoint the disable
      Here here the entire list LLDB teams.
    • Does obj-c not see the properties of an object of a Swift class? No problem:
      (lldb) po [array[0] name]
      
    • Does the variables window have 242 keys for the dictionary?
      We are looking fast:
      (lldb) po [[[_countries objectForKey:@"RU"] title] string]
      Россия
      
    • We put chisel and expand the LLDB command set


    Thank you all for reading. I hope someone discovered a couple of tricks for themselves.

    I did not describe each of the possibilities in detail, but I tried to show the direction to who I need - I can figure it out deeper or ask in the comments!

    Also popular now: