您的位置:首页 > 移动开发 > Objective-C

Effective Objective-C 2.0: Item 17: Implement the description Method

2013-12-07 11:09 471 查看


如何重载description 和 debugDescription 赞


Item 17: Implement the description Method

While debugging, you will often find it useful to print out an object to inspect it. One way is to write logging code to print out all the properties of the object. But the most usual way is to do something like this:

NSLog(@"object = %@", object);

When the string to log is built up,
object
will
be sent the description message and put in place of the
%@
in
the format string. So, for instance, if the object were an array, the output might look like this:

Click here to view code image

NSArray *object = @[@"A string", @(123)];

NSLog(@"object = %@", object);

Which outputs this:

object = (

"A string",

123

)

However, if you try this on a class of your own, you’ll often see something that looks more like this:

object = <EOCPerson: 0x7fd9a1600600>

That is not as helpful as the array! Unless you override
description
in your own class, the default implementation from
NSObject
will be
invoked. The method is defined on the
NSObject
protocol, but the
NSObject
class implements it. Many
methods are part of the
NSObject
protocol, and
it’s done this way because
NSObject
is not the
only root class.
NSProxy
is an example of another
root class, which conforms to the
NSObject
protocol. Because
methods like
description
are defined within the protocol, subclasses of these other root classes also must implement them. That implementation doesn’t do much, as you can see. What it does do is show the class
name alongside the memory address of the object. This would be useful to you only if you wanted to see whether two objects were exactly the same object. However, you can’t tell any more about the objects than that. It’s more likely that you are going to want
to know some more information about the object.

To make the output more useful, all you need to do is override
description
to return the string you want to represent your object. For example, consider a class to describe a person:

Click here to view code image

#import <Foundation/Foundation.h>

@interface EOCPerson : NSObject

@property (nonatomic, copy, readonly) NSString *firstName;

@property (nonatomic, copy, readonly) NSString *lastName;

- (id)initWithFirstName:(NSString*)firstName

lastName:(NSString*)lastName;

@end

@implementation EOCPerson

- (id)initWithFirstName:(NSString*)firstName

lastName:(NSString*)lastName

{

if ((self = [super init]))
{

_firstName = [firstName copy];

_lastName = [lastName copy];

}

return self;

}

@end

A typical
description
method implementation for this would be as follows:

Click
here to view code image

- (NSString*)description {

return [NSString stringWithFormat:@"<%@: %p,
\"%@ %@\">",

[self class], self,
_firstName, _lastName];

}

If this were to be used, the output for an object of type
EOCPerson
would now look like the following:

Click here to view code image

EOCPerson *person = [[EOCPerson alloc]

initWithFirstName:@"Bob"

lastName:@"Smith"];

NSLog(@"person = %@", person);

// Output:

// person = <EOCPerson: 0x7fb249c030f0, "Bob Smith">

This is clear and contains much more useful information. I suggest displaying the class name and pointer address just like the default implementation, simply because it’s useful to see sometimes. Although as you saw earlier,
NSArray
doesn’t
do this, and there certainly is no rule about it. What you choose to put in the description is whatever makes sense for the object in question.

A simple way to write a
description
method containing a lot of different bits of information is to piggyback on
NSDictionary
’s
description
method.
It returns something that looks like this:

{

key: value;

foo: bar;

}

This compact description can be used by forming a dictionary within your own
description
method and returning a string containing this dictionary’s
description
method.
For example, the following class describes a location with a title and coordinates (latitude and longitude):

Click here to view code image

#import <Foundation/Foundation.h>

@interface EOCLocation : NSObject

@property (nonatomic, copy, readonly) NSString *title;

@property (nonatomic, assign, readonly) float latitude;

@property (nonatomic, assign, readonly) float longitude;

- (id)initWithTitle:(NSString*)title

latitude:(float)latitude

longitude:(float)longitude;

@end

@implementation EOCLocation

- (id)initWithTitle:(NSString*)title

latitude:(float)latitude

longitude:(float)longitude

{

if ((self = [super init]))
{

_title = [title copy];

_latitude = latitude;

_longitude = longitude;

}

return self;

}

@end

It would be nice if the
description
method for this showed the title as well as the latitude and longitude. If an
NSDictionary were used
,
the
description
method would look like this:

Click
here to view code image

- (NSString*)description {

return [NSString stringWithFormat:@"<%@:
%p, %@>",

[self class],

self,

@{@"title":_title,

@"latitude":@(_latitude),

@"longitude":@(_longitude)}

];

}

The output would look like this:

Click here to view code image

location = <EOCLocation: 0x7f98f2e01d20, {

latitude = "51.506";

longitude = 0;

title = London;

}>

This is much more useful than just the pointer and class name, and all the properties of the object are nicely presented. You could also have used a string format that was formed of each of the instance variables, but the
NSDictionary
approach
is easier to maintain when more properties are added to the class and want to form part of the
description
method.

Another method to be aware of, also part of the
NSObject
protocol, is
debugDescription
,
whose purpose is very similar to
description
. The difference is that
debugDescription
is
the method called when you invoke the print-object command within the debugger. The default implementation within the
NSObject
class
simply calls directly through to
description
. For example, taking the
EOCPerson
class,
running an application in the debugger (LLDB in this case), and pausing at a breakpoint just after creating an instance looks like this:

Click here to view code image

EOCPerson *person = [[EOCPerson alloc]

initWithFirstName:@"Bob"

lastName:@"Smith"];

NSLog(@"person = %@", person);

// Breakpoint here

When the breakpoint has been hit, the debug console will be ready to receive input. The command
po
in
LLDB will perform the print-object function, yielding the following:

Click here to view code image

EOCTest[640:c07] person = <EOCPerson: 0x712a4d0, "Bob Smith">

(lldb) po person

(EOCPerson *) $1 = 0x0712a4d0 <EOCPerson: 0x712a4d0, "Bob Smith">

Note that the
(EOCPerson *) $1 = 0x712a4d0
is added by the debugger. The portion after that is what is returned from the debug-description method.

You may decide to make the normal description of an
EOCPerson
to be just the person’s name and then implement the debug-description method to provide the more thorough description.
In that case, the two methods would look like this:

Click
here to view code image

- (NSString*)description {

return [NSString stringWithFormat:@"%@
%@",

_firstName, _lastName];

}

- (NSString*)debugDescription {

return [NSString stringWithFormat:@"<%@:
%p, \"%@ %@\">",

[self class], self, _firstName, _lastName];

}

This time, running the same code as previously and using the print-object command will yield the following:

Click here to view code image

EOCTest[640:c07] person = Bob Smith

(lldb) po person

(EOCPerson *) $1 = 0x07117fb0 <EOCPerson: 0x7117fb0, "Bob Smith">

This output can be particularly useful when you don’t want all that extra information about the class name and pointer address to be visible in the normal description but still want the ability
to access it easily during debugging. An example of a class that does this from the Foundation framework is
NSArray
. For example:

Click here to view code image

NSArray *array = @[@"Effective Objective-C 2.0", @(123),
@(YES)];

NSLog(@"array = %@", array);

// Breakpoint here

In that case, running, stopping at the breakpoint, and printing out the array object looks like this:

Click here to view code image

EOCTest[713:c07] array = (

"Effective Objective-C 2.0",

123,

1

)

(lldb) po array

(NSArray *) $1 = 0x071275b0 <__NSArrayI 0x71275b0>(

Effective Objective-C 2.0,

123,

1

)


Things to Remember


Implement the description method to provide a meaningful
string description of instances.


If the object description could do with more detail
for use during debugging, implement
debugDescription
.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐