Debug and Tune Your App
Xcode provides all the facilities you need to make sure that your code is correct, that it performs well, and it uses device and network resources appropiately.
Ensure that Your Code Is Correct
Unit tests help you write robust and secure code by testing your application’s functionality. Xcode provides an easy-to-use and flexible unit-testing environment for ensuring that your code continues to work as designed as you make changes to it.
You can perform two types of unit tests: logic tests and application tests. With logic tests you can ensure that your code works at the lowest level, usually methods and single classes. With application tests you make sure that your app’s classes work as designed within the app.
Xcode makes it easy to create projects and products that include unit-testing support. But you can also add unit testing to existing projects and products.
To learn how to integrate unit testing in your project, see Xcode Unit Testing Guide.
Find Coding Mistakes
At any time in your code development, you can run the static analyzer to find potential semantic problems in your code. In addition, when you build your project, the compiler returns messages, warnings and errors that you can use to find and correct mistakes in your code.
Note that if you use the LLVM compiler, Fix-it scans your code and flags possible errors as you type (“Have Fix-it Flag Errors as You Type”).
Locate Possible Semantic Problems
Use static analysis to examine the semantics of your code to find potential problems with your program logic. Xcode lets you perform the analysis, examine the results, and edit your source files all within the workspace window.
You run the static analyzer by selecting the project you want to analyze in the project navigator and then choosing Analyze from the Product menu. You can use the Build pane of the scheme editor to specify which targets are included in the analysis.
Find flaws—potential bugs—in the source code of a project with the static analyzer built into Xcode. Source code may have subtle errors that slip by the compiler and manifest themselves only at runtime, when they could be difficult to identify and fix.
Locate and Display Build Issues
You can build and run the executable specified by the active scheme by clicking the action button at the left end of the toolbar. Other options, such as running without building or building without running, are available in the Product menu.
If the compiler finds any problems while building, the issue navigator opens. You can display problems by file or by type (Figure 8-1). Filters at the bottom of the navigator let you display only issues from the latest build, only issues associated with the current scheme, only errors (suppressing any warnings), or only issues that include text matching a string you enter in the filter field.
Do one of the following:
Use the issue pop-up menu at the right end of the jump bar.
The issue menu appears as a yellow triangle if the most serious issue listed is a warning () or as a red octagon if any errors are listed (). Choose an error from the menu or use the arrows to cycle through the errors.
Select any of the errors or warnings in the issue navigator to display in the source editor the line of code where the problem was discovered.
Click the number at the right margin of an issue to see a list of all the issues associated with the line of code.
Select a build in the build log.
The build results are displayed in the editor area.
Open an assistant editor and set it to Referenced Files.
Select an issue in the log to see it displayed in the assistant editor.
Double-click an issue in the log to see it displayed in a separate window or tab (depending on the double-click setting in the General pane of Xcode Preferences).
Whereas the issue navigator is the common place to go to see build errors and warnings, build logs are where you can see details about what happened during a build. For example, if you want to make sure files were built in the right order, ensure that a particular file got rebuilt, or track down some advanced project configuration problem, you can get the information you need in the build log.
Do one of the following:
Click the list icon () at the end of a build command line in the build log to see the full build command and results.
Option-click a build issue to open the full build results to that issue.
The issue highlights briefly when the full build results open.
As of Xcode 4.1, you can select what kind of content to view when debugging: source only (when available), disassembly only, or source and disassembly. The disassembly is the set of assembly-language instructions seen by the debugger while your program is running. Viewing the disassembly can give you more insight into what your code is doing when it’s stopped at a breakpoint. By viewing the disassembly and source code together, you can more easily relate the instruction being executed with the code you wrote.
Do one of the following:
Choose Product > Debug Workflow > Show Disassembly When Debugging.
The disassembly is displayed in the editor pane.
Open an assistant editor and choose Disassembly from the Assistant pop-up menu in the assistant editor jump bar.
The source and disassembly are shown in the split editor pane.
Although you can use the debugger to pause execution of your program at any time and view the state of the running code, it's usually helpful to set breakpoints before running your executable so that you can stop at known points and view the values of variables in your source code.
Add and Activate Breakpoints
To set breakpoints, open a source-code file and click in the gutter next to the spot where you want execution to stop. When you add a breakpoint to the code, Xcode automatically activates breakpoints, as indicated by the breakpoint-state button in the toolbar (active: inactive: ). You can toggle the state of breakpoints at any time by clicking the breakpoint-state button. You can also disable an individual breakpoint by clicking its icon. To remove a breakpoint completely, drag it out of the gutter.
You can set several options for each breakpoint, such as a condition, the number of times to pass the breakpoint before it’s triggered, or an action to perform when the breakpoint is triggered.
In addition to conditional breakpoints, which are triggered when a specific condition is met, you can create breakpoints that are triggered when a specific type of exception is thrown or caught, and symbolic breakpoints, which are triggered when a specific method or function begins execution.
Add an exception breakpoint to your project in the breakpoint navigator.
For existing breakpoints, you can edit options or change their scope—that is, the project or workspace in which the breakpoint appears.
Open the breakpoint navigator and Control-click the breakpoint whose options you want to edit.
From the shortcut menu, choose Edit Breakpoint.
In the Condition field of the options dialog, specify an execute condition for the breakpoint (if any).
The breakpoint is triggered only if the condition is true (for example, i == 24). You can use any variables that are in the current scope for that breakpoint.
You must cast an function calls to the appropriate return type.
Specify values for any other actions you want to set and click Done.
By default, a new breakpoint is local to the workspace you have open. If you add the project containing that breakpoint to another workspace, the breakpoint is not copied to the new workspace. You can assign breakpoints to other scopes, however. If you move a breakpoint to User scope, it appears in all of your projects and workspaces. If you move a breakpoint to a specific project, then you see this breakpoint whenever you open that project, regardless of which workspace the project is in.
In the breakpoint navigator, select the breakpoint or breakpoints you want to assign to a new scope.
Control-click one of the breakpoints, and from the shortcut menu, choose Move Breakpoints To.
If you have a workspace window open, your choices include Workspace, User, and all of the projects in the workspace:
If you have a project open without a workspace, your choices are User and the project you have open:
Choose Workspace to have the breakpoint appear only in this workspace. Choose User to have the breakpoint appear in all your workspaces and bare projects. Choose a specific project to have the breakpoint appear whenever that project is open, regardless of which workspace it’s in.
Your choice is reflected in the organization of the breakpoint navigator.
You can share any breakpoints with other users.
In the breakpoint navigator, select the breakpoint or breakpoints you want to share.
Control-click one of the breakpoints, and from the shortcut menu, choose Share Breakpoints.
Xcode moves the shared breakpoints into their own category in the breakpoint navigator:
A breakpoint with User scope cannot be shared. If you select a User breakpoint and choose Share Breakpoint from the shortcut menu, Xcode moves the breakpoint to the local (Workspace or individual project) category and shares it.
Set Preferences for Breakpoint Behavior
The “Run pauses” option in Behaviors preferences specifies what Xcode does when a breakpoint is triggered and the running application pauses. By default, Xcode opens the debug navigator and debug area, showing the location of the breakpoint in your code and the status of the application’s threads and stacks. However, you can customize this behavior, specifying various sorts of behaviors (such as a sound or announcement), which navigator to open, and whether to navigate to the first new issue. You can even design a tab with the layout you prefer, name the tab, and have Xcode re-create it when a breakpoint is triggered. For more information on Behaviors preferences, see “Customize Your Build and Run Workflow.”
Customize the Debug Area
If you pause execution or a breakpoint is triggered, the debug area opens, displaying the values of variables and registers plus the debug console (Figure 8-2). You can use the buttons at the right end of the debug area toolbar to display both the variables and console panes or to hide either one.
The Variables Pane
The variables pane displays variables and registers. You specify which items to display using the pop-up menu in the top-left corner of the variables pane:
Auto displays only the variables you’re most likely to be interested in, given the current context.
Local displays local variables.
All displays all variables and registers.
Use the search field to filter the items displayed in the variables pane.
The Console Pane
The console pane displays program output and lets you enter commands to the debugger tool. You specify the type of output the console displays with the pop-up menu in the top-left corner of the console pane:
All Output displays target and debugger output.
Debugger Output displays debugger output only.
Target Output displays target output only.
Control Program Execution
When you execute a program from Xcode, the debug bar appears at the bottom of the editor pane. You can use the debug bar to control program execution and to navigate through threads and stacks. You can also step through code and control execution in the source editor and you can suspend a thread in your running application.
To make it easier to debug full-screen applications, you can set Xcode to a window-in-front mode that lets you control program execution without obscuring your program window.
Step Through Code with the Debug Bar
The debug bar includes buttons to:
Open or close the debug area
Pause or resume execution of your code
Step over; that is, execute the current line of code and if it is a routine) return to the next line in the current file
Step in; that is, execute the current line of code and (if it is a routine) jump to its first line
Step out of a jumped-to routine; that is, complete the current routine and step to the next routine or back to the calling routine
Control-click to step through your code by assembly language instruction instead of by statement. The step icons change to show a dot rather than a line under the arrow.
Control-Shift-click to perform the action in the active thread while holding other threads stopped. The step icons show a dashed rather than solid line under the arrow.
Set a Location in the Debug Bar
if you are running an application for iOS 5.0 or later that uses Core Location, the debug bar has a location drop-down menu (Figure 8-3). To set a location in the Run action of the scheme editor, see “Location Simulation.”
Step Through Code in the Source Editor
While your code is paused at a breakpoint or because you clicked the pause button, you can continue execution or step through your code in the source editor.
To continue execution to a specific line of code, hold the pointer over the gutter next to the target code line until the continue-to-here icon appears, then click the icon.
To step to the next instruction, hold the pointer over the gutter next to the instruction pointer until the step-over icon appears, then click the icon.
Suspend a Thread
Suspend a thread to prevent it from running during your debugging session. You might suspend a thread if the thread is about to crash or if you want to prevent it from doing work that could interfere with the rest of your application.
Set Xcode Window Behavior
When you run your application from Xcode in order to debug it, you have both Xcode and your application open on your monitor. In some cases—for example, when you have a full-screen application and only one monitor—you might want to keep Xcode behind your application and use keyboard shortcuts to control window execution. Choose Product > Window Behavior > Xcode Behind to keep Xcode behind your application.
If you need to be able to see the debug area and other debugging information when you stop your application or it pauses at a breakpoint, however, you can instead choose Product > Window Behavior > Xcode In Front to keep Xcode in front of your application. In this case, while your application is running, the Xcode application shrinks to a minimum size, displaying a minimum set of controls. In Figure 8-4, the Xcode window is indicated with a red box.
Click the Console button to open the debug console while the application is running. Click the Focus button to allow the focus to switch between the application and Xcode.
When you pause execution or your program hits a breakpoint, Xcode expands to show the debug pane (Figure 8-5). When you continue execution, Xcode shrinks again.
Examine Threads, Stacks, Variables, and Memory
When you pause execution of your code (see “Control Program Execution”) or the running code triggers a breakpoint, Xcode opens the debug navigator, displaying the threads that were running when execution paused. Under each thread is the stack at that point in program execution. You can examine the threads and stacks in the debug navigator, using the filters at the bottom of the navigator to control the scope of threads and stacks so you can concentrate on the level of detail that is most useful to you. You can also navigate through threads and stacks in the debug bar of the debug area.
You can view variable values in the debug area or source editor, and you can use the debug area and editor pane together to view the contents of memory locations.
Set breakpoints at the locations where you want to examine threads and stacks.
Choose Run from the Product menu (or click the Run button).
When execution pauses, select a stack frame in the debug navigator.
The corresponding source file or disassembled object code is shown in the source editor.
The slider at the bottom of the debug navigator controls how much stack information the threads and stacks navigator displays. At the left end of the slider, the debug navigator shows only the top frame of each stack. At the right end, it shows all stack frames. Click the button at the left end of the slider () to toggle between displaying all the active threads or only threads that have your code in them (as opposed to system library code). The figure shows the debug navigator when execution has stopped at a breakpoint.
Filter threads and symbols to remove extraneous information and focus on your own code during a debugging session. The debug navigator sports two controls for filtering extraneous threads and program symbols: the thread filter and the call stack slider.
Inspect the value of a variable in a datatip to uncover a problem in the source code.
Capture OpenGL ES Frames
The debugging features for iOS OpenGL ES applications includes a facility for capturing and analyzing frames. When frame capture is enabled in the scheme for the application, the debug bar provides a control for entering the OpenGL ES frame debugging view.
To enable this feature, you must run the app on a device and the device must be running iOS 5.0 or later. Set the destination in the scheme menu to an iOS device and choose Edit Scheme from the scheme selector in the toolbar. Select the Run action, click the Options tab, and choose either Automatically Enabled or Enabled from the OpenGL ES Enable Frame Capture pop-up menu (Figure 8-6).
When you build and run your OpenGL ES application, the debug bar includes a frame capture button (Figure 8-7). Click that button to capture a frame.
You can use OpenGL frame capture to:
Inspect OpenGL ES state information.
Introspect OpenGL ES objects such as view textures and shaders.
Step through draw calls and watch the changes with each call.
Step through the state calls that precede each draw call to see exactly how the image is constructed.
Figure 8-8 shows a captured frame. The debug navigator has a list of every draw call and state call associated with that frame. The buffers associated with the frame are shown in the editor pane, and the state information is shown in the debug pane.
You can step through draw calls in the debug navigator, or by using the double arrows and slider in the debug bar (Figure 8-9).
When you use the draw call arrows or slider, you can have Xcode select the stepped-to draw call in the debug navigator. To do so, Control-click below the captured frame and choose Reveal in Debug Navigator from the shortcut menu (Figure 8-10).
You can also use the shortcut menu to toggle between a standard view of the drawing and a wireframe view (Figure 8-11). The wireframe view highlights the element being drawn by the selected draw call.
Open the Assistant editor to see the objects associated with the captured frame. In the Assistant editor you can choose to see all the objects, only bound objects, or the stack. Open a second Assistant editor pane to see both the objects and the stack for the frame at the same time (Figure 8-12).
Double-click an object in the Assistant editor to see details about that object. For example, if you double-click a texture object, you can see the texture in detail. Figure 8-13 shows the data in a vertex array (VAO) object.
Fix Bugs in Your App
This section provides an overview of the Xcode debugging facilities and describes how to view console output.
Part of your debugging workflow may require viewing or manipulating data your app writes in its sandbox. For example, you may need to edit the data that the app has stored so you can recreate a particular condition to test it. See “To design and implement user interfaces at the same time” for details about manipulating your app’s data.
Debugging Facilities Overview
Xcode provides several integrated debugging facilities, which provide both a general view of your app’s code and details when you need them.
The debug navigator. This navigator (on the left of the workspace window) shows your app’s threads and stack frames, which you use to navigate your running code. Selecting an item in the debug navigator opens its file in the source editor.
The source editor. You use the source editor (in the editor area) to debug your code right in your code. It provides most of the debugging features you need. You can:
View the value of variables by holding the pointer over them
Continue execution up to a particular code line
Step in to, out of, or over function or method calls
The debug area. The debug area (below the source editor) presents a variables list and a console pane to show your program’s console output. You can also issue debugger commands in the console pane. You use the debug bar, at the top of the debug area, to control your program’s execution by pausing, continuing, and stepping through code.
Viewing Console Output and Device Logs
The iOS frameworks, such as UIKit, send log entries to the console to indicate, among other things, when an unexpected event occurs. You can emit console messages in your iOS apps, too. One way to emit console messages is to use the
NSLog function. Console logs can help you analyze your app’s logic and track down bugs.
When running your app in a simulator, you can access the app’s console logs in the Console app (located in
/Applications/Utilities). When you run the app on a device, log entries from your app appear in the Xcode Organizer.
To view a device’s console output:
Choose Window > Organizer to open the Organizer window.
Click Devices to display the devices organizer.
In the section for the device whose logs you want to view, select Device Logs.
The Device Logs pane in the Organizer contains information about app crashes. You may have to unplug your device and plug it in again to refresh the crash list.
For more information about crash logs, see Understanding and Analyzing iPhone OS Application Crash Reports.
Troubleshooting: Debugging Issues
Xcode Displays the “Unknown iOS Detected” Dialog When You Plug In a Device
To successfully debug apps on the device, you must have Xcode collect the information it needs from the device.
Tune the Performance of Your App
Optimizing your app’s performance is an important part of the development process. This optimization is especially important in iOS-based devices, which, although powerful computing devices, do not have the memory or CPU power that desktop or portable computers possess. You also have to pay attention to your app’s battery use because it directly impacts the user’s battery-life experience.
This section introduces Instruments, the Xcode graphical tool that you use to measure and tune your app’s performance.
For general performance guidelines, see "Performance Tuning".
The Instruments App
With the Instruments app, you gather a variety of app performance metrics, such as memory and network use. You can gather data from iOS apps running in a simulator or a development device.
It is important that your iOS apps use device resources as efficiently as possible to provide a satisfactory experience for your customers. For example, your app should not use resources in a way that makes the app feel sluggish to users or that drains their batteries too quickly. Apps that use too much memory run slowly. Apps that rely on network access for their operation must use it as sparingly as possible because powering up the radios for network communications is a significant drag on the battery.
The Instruments app provides an advanced data gathering interface that lets you know exactly how your app uses resources, such as the CPU, memory, the file system, and so on.
Instruments uses software-based data-gathering tools, known as instruments, to collect performance data. An instrument collects a specific type of data, such as network activity or memory usage. A trace document contains one or more instruments that collect data about an app.
Although most iOS apps can run in a simulator and you can test most design decisions there, a simulator cannot completely match the behavior of a device. For example, it doesn’t replicate a device’s performance characteristics such as CPU speed or memory throughput. To effectively measure your app’s performance on user devices, you must use an actual device. Only on a device can you get an accurate representation of the runtime environment (in terms of processor speed, memory limitations, specialized hardware requirements, and the like).
Behavior you cannot test in a simulator and therefore must test on devices:
Events involving more than two fingers.
Actual accelerometer readings. Although you can access your computer’s accelerometer (if it has one) through the UIKit framework, its readings differ from the accelerometer readings on a device. This discrepancy stems largely from the difference in the positioning of the screen in relation to the rest of the hardware between computers and iOS-based devices.
Rendering by OpenGL ES. OpenGL ES uses renderers on devices that are slightly different from those it uses in a simulator. Therefore, a scene in the simulator and the same scene on a device may not be identical at the pixel level. See “Drawing with OpenGL ES” in iOS App Programming Guide for details.
To measure your app’s performance on a device:
Build and run your app on the device as described in “Simultaneously Design and Implement User Interface Objects Using Multiple Editors.”
Stop the app.
The Instruments app is located at
<Xcode>refers to the installation location of the Xcode toolset.)
Choose a template, such as Activity Monitor, to create the trace document.
From the Target toolbar menu, choose the device containing the app from which you want to collect performance data.
Add or remove instruments from the trace document to collect the data you want.
From the Target toolbar menu, choose the app to launch (the same app you ran in step 1).
Click Record to start collecting data.
Use your app, exercising the areas you want to examine.
To learn more about measuring and analyzing app performance, see Instruments User Guide.
© 2013 Apple Inc. All Rights Reserved. (Last updated: 2013-04-23)