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,
objectwill
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
descriptionin your own class, the default implementation from
NSObjectwill be
invoked. The method is defined on the
NSObjectprotocol, but the
NSObjectclass implements it. Many
methods are part of the
NSObjectprotocol, and
it’s done this way because
NSObjectis not the
only root class.
NSProxyis an example of another
root class, which conforms to the
NSObjectprotocol. Because
methods like
descriptionare 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
descriptionto 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
descriptionmethod 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
EOCPersonwould 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,
NSArraydoesn’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
descriptionmethod containing a lot of different bits of information is to piggyback on
NSDictionary’s
descriptionmethod.
It returns something that looks like this:
{
key: value;
foo: bar;
}
This compact description can be used by forming a dictionary within your own
descriptionmethod and returning a string containing this dictionary’s
descriptionmethod.
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
descriptionmethod for this showed the title as well as the latitude and longitude. If an
NSDictionary were used,
the
descriptionmethod 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
NSDictionaryapproach
is easier to maintain when more properties are added to the class and want to form part of the
descriptionmethod.
Another method to be aware of, also part of the NSObject
protocol, isdebugDescription
,
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 imageEOCPerson *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
poin
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 = 0x712a4d0is 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
EOCPersonto 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.
相关文章推荐
- Item 17: Implement the Standard Dispose Pattern(Effective C#)
- Item 25: Implement the Event Pattern for Notifications(Effective C#)
- must implement the inherited abstract method 问题解决
- 为什么LoadPostData 执行不了, why the LoadPostData method was not be implement
- The server method '' failed with the following error:" .... and no error description
- Opening nodes in a Flex Tree control using the expandItem() method
- Item 18:Implement the Standard Dispose Pattern
- Swift 2.0 : 'enumerate' is unavailable: call the 'enumerate()' method on the sequence
- 当编写Servlet时出现type Status report message HTTP method GET is not supported by this URL description The
- Description Resource Path Location Type Access restriction: The method getCallerClass(int) from
- The method process(ItemDetailInfo, Task) of type ItemRuKuUtils must override a superclass method的解决方
- The number of method references in a .dex file cannot exceed 64k API 17
- Swift 2.0 : 'enumerate' is unavailable: call the 'enumerate()' method on the sequence
- The ServiceClass object does not implement the required method in the following form
- Implement a stack that pops out the mostfrequently added item. Stack supports 3 functions – push,
- The type new ActionListener(){} must implement the inherited abstract method ActionListener.actionPe
- org.apache.axis2.AxisFault: The ServiceClass object does not implement the required method in the following form: OMElement get
- The method xxx of type xxx must override or implement a supertype
- 提示有错:The type new View.OnClickListener(){} must implement the inherited abstract method View.OnClick
- Using the isBranch() method to determine if a Tree item is a branch or leaf