前言:在实际开发中,我们经常会使用断点调试,来获得在某一步操作之后,程序中某一对象的所有属性值。
但是当需要频繁更改并实时获取该对象属性值得时候,使用断点调试会浪费大量的等待时间,相比之下,使用NSLog暴力输出会显得更方便快速,这时我们就要结合重写对象的description方法来打印当前属性值。
问题阐述:
一般情况下,我们会使用断点调试的方式,来获取某一操作之后,程序中某一对象的属性值。
例如,新建Student类,并对其进行赋值:
#import <Foundation/Foundation.h>
@interface StudentModel : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *city;
@property (nonatomic, copy) NSString *country;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) NSInteger sex;
@end
- (void)setData{
_model = [[StudentModel alloc]init];
_model.name = @"Samson";
_model.city = @"FuYang";
_model.country = @"China";
_model.age = 24;
_model.sex = 1;
}
屏幕快照 2017-07-31 下午2.15.23.png
在图中所示位置设置断点,进行调试,可以得到当前该对象的所有属性值:
屏幕快照 2017-07-31 下午2.16.59.png但是当我们需要在项目运行时,频繁更改或者获取对象的属性值时,使用断点调试会浪费不少的时间,这是我们想到了NSLog打印语句。在上图中去掉断点,查看此时的打印结果:
屏幕快照 2017-07-31 下午2.21.49.png这时NSLog语句只打印出了对象的类名和地址,显然不是我们想要的输出信息。
解决方式:
我们一般通过重写对象的description方法来打印出所需要的属性值。
例如我们需要获得Student对象的name、city和age属性值时,通常重写方式为:
- (NSString *)description{
return [NSString stringWithFormat:@"name:%@\n city:%@\n age:%ld",self.name,self.city,self.age];
}
此时查看NSLog打印语句输出信息:
屏幕快照 2017-07-31 下午2.30.09.png正确打印出我们需要的结果。
问题思考:这样做能打印出我们设定好的属性值,但是当我们动态为该对象添加或者删除属性时,这种方式却不能实时打印出对象的所有属性值,这时我们要对处理方式进行优化。
方法优化:
什么是objc_property_t:
objc_property_t是表示Objective-C声明的属性的类型,其实际是指向objc_property结构体的指针,其定义如下:
/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;
我们通过objc_property_t在description方法中动态获取对象当前的属性列表,并进行赋值打印:
- (NSString *)description{
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
uint count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
NSString *name = [NSString stringWithFormat:@"%s",property_getName(properties[i])];
id value = [self valueForKey:name] ?:@"nil";
[dict setObject:value forKey:name];
}
free(properties);
return [NSString stringWithFormat:@"<%@:%p>:%@",[self class],self,dict];
}
查看此时打印结果:
屏幕快照 2017-07-31 下午2.50.40.png如果要将断点调试时po命令打印信息也改为对象所有属性值,则需要重写对象的debugDescription方法:
- (NSString *)debugDescription{
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
uint count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
NSString *name = [NSString stringWithFormat:@"%s",property_getName(properties[i])];
id value = [self valueForKey:name] ?:@"nil";
[dict setObject:value forKey:name];
}
free(properties);
return [NSString stringWithFormat:@"<%@:%p>:%@",[self class],self,dict];
}
在实际使用时,我们不可能在每个对象中都对这两个方法进行重写。这时,可以自定义工具类对其进行方法重写:
- (NSString *)description{
return [MyControl getDescriptionWithObjc:self];
}
并且在方法实现中进行相关的逻辑处理:
+ (NSString *)getDescriptionWithObjc:(id)objc{
//如果不是要打印自定义类的属性,直接实现原有的description方法即可
if ([objc isKindOfClass:[NSArray class]] || [objc isKindOfClass:[NSDictionary class]] || [objc isKindOfClass:[NSString class]] || [objc isKindOfClass:[NSNumber class]]) {
return [objc description];
}
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
uint count;
objc_property_t *properties = class_copyPropertyList([objc class], &count);
for (int i = 0; i < count; i++) {
NSString *name = [NSString stringWithFormat:@"%s",property_getName(properties[i])];
id value = [objc valueForKey:name] ?:@"nil";
[dict setObject:value forKey:name];
}
free(properties);
return [NSString stringWithFormat:@"<%@:%p>:%@",[objc class],objc,dict];
}
网友评论