description

396 阅读2分钟

调试程序时,经常需要打印并查看对象信息。一种方法是编写代码把对象的全部属性都输出到日志中。最常用做法像下面这样:

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

1 在构建需要打印到日志的字符串时, object 对象会收到 description 消息, 该方法所返回的描述信息将取代 “格式字符串”(format string) 里的 “%@”。比方说, object 是个数组,若用下列代码打印其信息:

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

其输出结果:
object = ("A string",
          123
)
然而,如果在自定义的类上面这么做, 那么输出的信息确下面这样:
object = <EOCPerson: 0x7fd9a1600600>

与object为数组时所输出的信息相比,上面这种内容不太有用。除非在自己的类里覆写 description 方法,否则打印信息时就会调用NSObject类所实现的默认方法。此方法定义在NSObject协议里,不过NSObject类也实现了它。因为NSObject并不是唯一的“根类”,所以许多方法都要定义在NSObject协议里。比方说,NSProxy也是一个遵从了NSObject协议的“根类”。由于description等方法定义在NSObject协议里,因此像NSProxy这种“根类”及其子类也必须实现它们。

要想输出更为有用的信息也很简单,只需覆写description方法并将描述此对象的字符串返回即可。例如:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MJPerson : NSObject

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

- (id)initWithFirstName: (NSString *)firstName lastName:(NSString *)lastName;

@end

NS_ASSUME_NONNULL_END

#import "MJPerson.h"

@implementation MJPerson

- (id)initWithFirstName: (NSString *)firstName lastName:(NSString *)lastName {
    if(self = [super init]){
        _firstName = [firstName copy];
        _lastName = [lastName copy];
    }
    return self;
}

//该类的description方法通常可以这样实现:
- (NSString *)description {
    return [NSString stringWithFormat:@"<%@: %p, \"%@ %@\">",
            [self class], self, _firstName, _lastName];
}

@end

假如按上面的代码来写,那么ZZBPerson 对象就会输出如下格式信息:

#import "ViewController.h"
#import "MJPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    MJPerson *person = [[MJPerson alloc] initWithFirstName:@"Bob" lastName:@"Smith"];
    NSLog(@"person = %@", person);
    
    //打印结果:
    //<MJPerson: 0x600002109440, "Bob Smith">
}

@end

显而易见就比覆写之前所输出的信息更加清楚,更为有用。

NSObject协议中还有个方法要注意,那就是debugDescription, 此方法的用意与description非常相似。二者的区别在于,debugDescription方法是开发者在调试器中以控制台命令打印对象时才调用的。在NSObject类的默认实现中,此方法只是直接调用description。