Tải bản đầy đủ (.pdf) (7 trang)

Lập trình Wrox Professional Xcode 3 cho Mac OS part 64 docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.1 MB, 7 trang )

484

CHAPTER 18 DEBUGGING
To delete an expression, select it in the Expressions window and press the Delete key.
Expressions are very useful for examining the contents of array values. An expression like stack[1]
examines the second element in the stack array. The expression buffer[index] examines whichever
element the variable index refers to.
DATA FORMATTERS
The summary column in the variables pane is designed to present a compact, informative
explanation of the value or object. For some objects, like strings, the summary value is obvious.
For objects like a collection it might display the size of the collection — not a lot of detail, but
still informative. For an opaque FSRef structure, it might convert that structure into a readable
fi lename — very informative, indeed. This descriptive transformation is done using data formatters.
Xcode includes many data formatters, but they won ’ t help with any object that you defi ne. You can
create your own data formatters to summarize complex data structures and objects in the debugger.
Creating a Custom Data Formatter
Creating your own data formatters is very easy. It is really nothing more than a format string with
placeholders for values or expressions derived from the variable being summarized. Any regular
text in the data formatter is displayed verbatim. There are two kinds of placeholders: references and
expressions.
References are delimited by two percent characters ( %reference% ). A reference can refer to a single
member variable by name. If the variable is contained in a substructure, use the appropriate period
(.) separated variable name. You cannot use operators such as pointer dereferences or array indexes.
For that, you need to use an expression. Taking the class that ’ s defi ned in Listing 18 - 3, the reference
to the integer
record_no of DataBlock class would be %header.record_no% .
LISTING 18 - 3: Sample class

typedef struct {
int record_no;
unsigned long checksum;


} BlockHeader;

@interface DataBlock : NSObject
{
@public
BlockHeader header;
NSMutableData* data;
}
An expression is any debugger expression; the same kind of expression that you can add to the
Expressions window. In fact, it ’ s good to think of an expression in a data formatter as an expression
in the Expressions window, as you ’ ll see in moment.
Expressions are contained between matching braces ( { expression } ). Unlike references, expressions
do not assume the context of the value being examined. To refer to the object being examined, use
c18.indd 484c18.indd 484 1/22/10 12:55:49 PM1/22/10 12:55:49 PM
Download at getcoolebook.com
the $VAR macro in the expression. $VAR will be replaced with the name of the variable when the data
formatter is evaluated. Using the previous class as an example again, the expression to access the
record_no value would be {$VAR.header.record_no} . If you ’ re now guessing that you can refer to
other variables in the context of the current stack frame, you ’ re correct. However, this isn ’ t a good
idea, which is explained later. Limit your evaluation to the structure or object being examined.
The advantage of expressions over references is that they are more expressive. You can perform
math, include conditionals, and even call member functions. Again using the class defi ned in
Listing 18 - 3, here are some valid expressions:
{$VAR.header.record_no}
{$VAR.header.checksum & 0x0000ffff}
{$VAR.data?(int)[$VAR.data length]:0}
Combining these two techniques, you can now create a data formatter for the DataBlock object
type. Start by running the program and stopping the debugger with an instance of the DataBlock
class in scope. Make sure that Run ➪ Variables View ➪ Enable Data Formatters is checked. Select
the DataBlock variable in the variables pane and choose the Run ➪ Variables View ➪ Edit Summary

Format. The debugger lets you edit the data formatter for the variable. Now you can enter the data
formatter string shown in Figure 18 - 31.



FIGURE 18-32
FIGURE 18-31
After it has been entered, this text becomes the data formatter used for every instance of the
DataBlock class. The debugger resolves the references and expressions for each instance, creating
the more informative summary shown in Figure 18 - 32.
The syntax used for references and expressions can be extended to obtain other information
about the value or expression. The fi nal display value can be something different from the value to
which the expression evaluates. The other types of information that can be extracted from an
expression are chosen using one of the column selectors listed in the following table. The “ column ”
Data Formatters

485
c18.indd 485c18.indd 485 1/22/10 12:55:50 PM1/22/10 12:55:50 PM
Download at getcoolebook.com
486

CHAPTER 18 DEBUGGING
you are selecting is one of the columns in the variables pane or the Expressions window. In essence,
the result of a reference or expression is treated as if it had been entered into the Expressions
window. The column selector lets you choose which column in the window will be used as the result
of the expression. You can think of an expression result as an object with four properties (value,
name, type, and summary) — the column selector lets you choose which property to display.
COLUMN SELECTOR DESCRIPTION
{ expression }:v ,
% reference %:v

The value of the expression — that is, the primitive numerical value that
would appear in the Value column of the Expressions window. This is the
default column. Omitting the column selector is equivalent to using
:v .
{ expression }:t ,
% reference %:t
The type of the fi nal data object to which the expression evaluates. A
numerical expression would result in a primitive data type, such as int or
double. The type of an expression that refers to a member variable, or
calls a function, will be the type of the expression ’ s result. For example,
the expression
{[$VAR owner]}:t would display the type of the object
returned by the method owner.
{ expression }:s ,
% reference %:s
This selector results in the text that would appear in the Summary column
of the expression. Because the Summary column can be formed using
data formatters, this is a way of using other data formatters in portions
of your data formatter. You can only use this on expressions that have a
summary display. Expressions that result in primitive values do not have
any content in their Summary column.
{ expression }:n ,
% reference %:n
The name of the variable or expression that would appear in the
Expression column of the Expressions window. The column is self -
referential and not particularly useful.
The type column ( :t ) can be useful for displaying the type or class of a member value. For
example, if you have a class that manages a collection of homogenous objects, a data formatter
of collection of {[$VAR lastObject]}:t would tell you what kind of objects your collection
contains.

The summary column selector is the most useful. You can use it to construct data formatters from
other data formatters.
To get a feel for creating your own data formatters, look at the following example. Let ’ s assume
you have a project with the DataBlock class shown in Listing 18 - 3, and a subclass named
StampedDatablock, shown in Listing 18 - 4.
LISTING 18 - 4: DataBlock subclass

@interface StampedDataBlock : DataBlock
{
@private
NSCalendarDate* createdDate;
NSCalendarDate* modifiedDate;
}
c18.indd 486c18.indd 486 1/22/10 12:55:51 PM1/22/10 12:55:51 PM
Download at getcoolebook.com
To create a custom data formatter for these two classes, follow these steps:
1. Build the application, start it under the control of the debugger, and stop at a breakpoint
where a DataBlock and StampedDataBlock object are in scope, as shown in Figure 18 - 33.
2. Set the data formatter for the block variable to record %header.record_no%,
{(int)[$VAR.data length]} bytes .
3. Set the data formatter for the trackedBlock variable to {(DataBlock*)$VAR}:s, created
%createdDate%:s .
4. Continue stepping through the program.
FIGURE 18-33
The fi rst data formatter created for the DataBlock class summarizes its contents by accessing its
record_no and data instance variables. The debugger now presents a much friendlier summary of
the object ’ s state.
The StampedDataBlock formatter is a little trickier. The StampedDataBlock class does not
inherit the data formatter for DataBlock. Data formatters for each type are independent of one
another.

Two problems are inherent in creating a data formatter for the new subclass. First, you
don ’ t want to repeat everything you wrote for the DataBlock formatter. Secondly, you don ’ t
want to write a formatter for the NSCalendarDate member object. The summary column selector
lets you avoid both of these problems by setting the data formatter for the StampedDataBlock
class to
{(DataBlock*)$VAR}:s, created %createdDate%:s . The fi rst expression casts the
object to an instance of its superclass and obtains text that would appear in its Summary column,
effectively calling the data formatter you created for the DataBlock class. The second reference
obtains the value of createdDate and inserts what would appear in its Summary column,
essentially using Xcode ’ s built - in data formatter. The fi nal result, shown in Figure 18 - 34, is a
data formatter that extends the data formatter of its superclass using a built - in data formatter
supplied by Xcode.
Data Formatters

487
c18.indd 487c18.indd 487 1/22/10 12:55:52 PM1/22/10 12:55:52 PM
Download at getcoolebook.com
488

CHAPTER 18 DEBUGGING
Troubleshooting Data Formatters
Data formatters can be very useful during debugging. You can create data formatters that quickly
summarize the state of complex objects. This allows you to concentrate on the high - level logic of your
application, rather than spending all of your time digging through the member variables of objects
trying to decode their content. Writing data formatters, however, can be a frustrating experience.
If there is anything the debugger doesn ’ t like about your data formatter, it won ’ t use it. The following
table lists some of the more common problems you can encounter while creating data formatters:
TYPE OF PROBLEM SOLUTION
Syntax Be extra careful about the syntax of your expressions and references.
Quotes Double quotes in the body of an expression must be escaped with a

backslash. Example: name “ {[$VAR getProperty:\ “ name\ “ ]}:s ”
.
Notice that the quotes inside the expression are escaped, but not in
the text outside the expression.
Unknown types The debugger often does not know the data type of values returned
by functions. If you have any doubts, cast the result:
author
{(NSString*)[$VAR authorName]}:s
Execution problems Expressions that call functions have to function perfectly. The
formatter name {[$VAR name]}:s will fail if the method name throws
an exception, tries to access a NULL variable, can ’ t allocate memory, or
any of a limitless number of similar run time problems. The functions
that you call using data formatters should be extremely defensive.
Null summary You cannot use the
:s column selector if the expression results in a
data type that has no summary column content.
Invalid references Expressions that use other variables in the current stack frame
context will fail when interpreted in a di erent stack frame or
execution context where those variables don ’ t exist. Data formatters
should concern themselves only with examining the contents of the
structure or object.
FIGURE 18-34
c18.indd 488c18.indd 488 1/22/10 12:55:53 PM1/22/10 12:55:53 PM
Download at getcoolebook.com
TYPE OF PROBLEM SOLUTION
ZeroLink, dynamically
loaded libraries
This is yet one more situation where dynamically loaded libraries
can trip you up. Expressions executed in the debugger will not cause
unreferenced symbols to load. If your application hasn ’ t caused a

symbol or function to load yet, a data formatter that uses that function
or type will fail.
Temporary Objects (For Objective - C programmers) Be warned that creating auto - released
objects in your data formatters may result in memory leaks. An example
would be
{(NSString*)[NSString stringWithCharacters:
$VAR.uStr.unicode length:$VAR.uStr.length] }:s . The
problem here is that the NSString object is created in the context of
the debugger and has no auto - release pool. You will see a “ leak ”
message in the debugger log.
Side E ects Data formatters can call functions in your application. Side e ects, such
as altering instance variables or releasing objects, can have unexpected
consequences in your application and your debugging e orts.
If you are having problems getting a formatter to work, break it down into its individual
components and subexpressions and try each one at a time. Slowly build up the expression until you
get it working or fi nd the element that thwarts your efforts. Try expressions without the column
selector. Cast return values liberally. Replace macros and function calls with constants. Turn off
ZeroLink. Add the same function call to your code and try debugging it.
Data formatters you defi ne are stored in the ~/Library/Application Support/Apple/Developer
Tools/CustomDataViews/CustomDataViews.plist fi le. Data formatters are global to all projects
and are not stored in the project document. Sharing data formatters with other developers will
require some copying and pasting, or you may just want to exchange CustomDataViews.plist fi les.
Beyond Data Formatter Strings
Although data formatters can do a lot, they are limited to what can be expressed in a format
string. If you need a data formatter that exceeds these capabilities, you can develop your own data
formatter plug - in. The descriptions for doing so are in the DataFormatterPlugin.h fi le, buried
inside the Xcode application itself at /Developer/Applications/Xcode.app/Contents/PlugIns/
GDBMIDebugging.xcplugin/Contents/Headers/DataFormatterPlugin.h . This fi le contains
detailed information about formatter strings, the format of CustomDataViews.plist , and how to
create a data formatter plug - in, among other topics.

In brief, you create a data formatter plug - in by creating a bundle. The bundle contains its own
CustomDataViews.plist fi le. Unlike data formatter strings that you type into the debugger
window, the data formatter strings in the bundle ’ s CustomDataViews.plist fi le can call any of
the functions defi ned in the plug - in bundle. The sample ManagedObjectDataFormatter project
produces a data formatter plug - in for managed objects. You can fi nd it by searching the Xcode
documentation for ManagedObjectDataFormatter. Use this project as a template for creating your
own data formatter plug - ins.
Data Formatters

489
c18.indd 489c18.indd 489 1/22/10 12:55:54 PM1/22/10 12:55:54 PM
Download at getcoolebook.com
490

CHAPTER 18 DEBUGGING
Object Descriptions
Like data formatters, many object - oriented languages have adopted conventions for converting any
object into a textual representation. In Java, this is the toString() function. Objective - C uses the
- [NSObject description] method. If you are using an object that supports one of these standards,
you can use the Run ➪ Variables View ➪ Print Description to Console command. The debugger
invokes the standard “ to string ” function on the object and sends the result to the debugger console.
WATCHPOINTS
Watchpoints are breakpoints for data. You can make any variable a watchpoint. Whenever the
debugger detects that the value of that variable has changed, it stops your application.
Watchpoints sound great, but they are fairly limited. The biggest problem is that your application
can ’ t execute any code where the watchpoint variable is out of context, so they are mostly useful for
global variables that are always in scope and for catching state changes in a loop.
You set a watchpoint by fi rst selecting a variable in the variables pane. Choose the Run ➪ Variables
View ➪ Watch Variable command. This places a magnifying glass icon next to the variable as shown
in Figure 18 - 35. Start the program executing again, and it breaks at the point just before the variable

is altered with a dialog box explaining what is about to happen, also shown in Figure 18 - 35.
FIGURE 18-35
You can choose to acknowledge the event and leave the watchpoint set, or disable the watchpoint
by clicking the Disable button. Watchpoints are automatically deleted whenever your application
exits the context where the watchpoint variable exists. Watchpoints are not retained between debug
sessions.
You can create an effect similar to a watchpoint using a breakpoint conditional
like
i!=0 . It ’ s not as convenient as a watchpoint, but it ’ s more durable.
To remove a watchpoint, select the variable being watched and choose Run ➪ Variables View ➪
Watch Variable again to remove the check mark.
c18.indd 490c18.indd 490 1/22/10 12:55:54 PM1/22/10 12:55:54 PM
Download at getcoolebook.com

×