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

如何重载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:

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 NSObjectis 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:

#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:

- (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:

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 owndescription method 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):

#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:

- (NSString*)description {
    return [NSString stringWithFormat:@"<%@: %p, %@>",
            [self class],
            self,
            @{@"title":_title,
              @"latitude":@(_latitude),
              @"longitude":@(_longitude)}
           ];
}

The output would look like this:

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 thedescription method.

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

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:

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:

- (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:

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:

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:

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

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

Image If the object description could do with more detail for use during debugging, implement debugDescription.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值