发布自米高 | Michael - 博客园,源地址:http://www.cnblogs.com/michaellfx/p/4232205.html,转载请注明。

本文结构

关键字:Objective-C OC description函数 自动打印属性及属性值 运行时枚举成员变量

基础实现

使用NSLogpo,Xcode默认调用对象的description方法,若没实现,则打印对象的地址,不方便查看对象的状态。特别地,在RESTful编程中,服务器返回的JSON对象往往具有较多属性,若每个对象建立一个类,并为这些类一一实现description方法,工作量大且是重复性工作,对我们码农没实质帮助,还容易漏掉部分属性。像这种重复性工作,还是由计算机去做更合适。

实现自动化description的基本思路是,基类实现此方法,子类只需按需定义属性即可。

基类实现description的算法是,通过运行时读取对象运行时所属的类(注:当使用KVO时,在有观察者的情况下,运行时将为被观察的类生成一个新类,再返回新类的类型,这是ISA混写的一种具体应用。)对象及所有成员变量,再由KVC读写成员变量的值。

BaseModel.m
////////////////////////////////////////////////////////////////////////////
- (NSDictionary *)mapPropertiesToDictionary {
// 用以存储属性(key)及其值(value)
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
// 获取当前类对象类型
Class cls = [self class];
// 获取类对象的成员变量列表,ivarsCount为成员个数
uint ivarsCount = 0;
Ivar *ivars = class_copyIvarList(cls, &ivarsCount);
// 遍历成员变量列表,其中每个变量为Ivar类型的结构体
const Ivar *ivarsEnd = ivars + ivarsCount;
for (const Ivar *ivarsBegin = ivars; ivarsBegin < ivarsEnd; ivarsBegin++) {
Ivar const ivar = *ivarsBegin;
// 获取变量名
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
/*
若此变量声明为属性,则变量名带下划线前缀'_'
比如 @property (nonatomic, copy) NSString *name;则 key = _name;
为方便查看属性变量,在此特殊处理掉下划线前缀
*/
if ([key hasPrefix:@"_"]) key = [key substringFromIndex:1];
// 获取变量值
id value = [self valueForKey:key];
// 处理属性未赋值属性,将其转换为null,若为nil,插入将导致程序异常
[dictionary setObject:value ? value : [NSNull null]
forKey:key];
}
if (ivars) {
free(ivars);
}
return dictionary;
}

枚举属性完成了。需要说明的是,由于业务中类层次只有两层,故上述代码不处理父类属性。若有需要,可通过class_getSuperclass()方法枚举父类成员变量,在递归父类时,递归出口为当前枚举的类等于根类NSObject,即cls == [NSObject class]。剩下的是实现基类的description方法。

BaseModel.m
////////////////////////////////////////////////////////////////////////////
- (NSString *)description {
NSMutableString *str = [NSMutableString string];
NSString *className = NSStringFromClass([self class]);
NSDictionary *dic = [self mapPropertiesToDictionary];
[dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[str appendFormat:@"%@ = %@\n", key, obj];
}];
return str;
}

至此,功能基本完成。子类只需继承基类,在.h文件中声明属性即可。

User.h
////////////////////////////////////////////////////////////////////////////
#import "BaseModel.h" @interface UserState : BaseModel @property (nonatomic, copy) NSString *name; @end

虽然功能实现了,前面的实现还有性能优化空间。

性能优化

每次调用description,都要调用mapPropertiesToDictionary,显然无此必要。故,优化思路是,在基类中维护一个静态哈希表,子类第一次使用description方法才调用mapPropertiesToDictionary,往后都从哈希表中检索已构造的属性值字典。下面给出一种参考实现。

BaseModel.m
////////////////////////////////////////////////////////////////////////////
static NSMutableDictionary *modelsDescription = nil; // 在load或initialize方法中初始化哈希表,在此为字典。
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
modelsDescription = [NSMutableDictionary dictionary];
});
} // 修改description构造字典处理
- (NSString *)description {
//...
if (value) {
dic = (NSDictionary *)value;
} else {
dic = [self mapPropertiesToDictionary];
[modelsDescription setObject:dic forKey:className];
}
//...
}

关于根类NSObject的loadinitialize之间的区别,下次再作讲解。

参考

Objective-C Runtime Reference

Objective-C运行时编程 - 实现自动化description方法的思路及代码示例的更多相关文章

  1. iOS运行时编程(Runtime Programming)和Java的反射机制对比

    运行时进行编程,类似Java的反射.运行时编程和Java反射的对比如下:   1.相同点   都可以实现的功能:获取类信息.属性设置获取.类的动态加载(NSClassFromString(@“clas ...

  2. Objective-C 2.0的运行时编程

    Objective-C 2.0 的运行时环境叫做Morden Runtime,iOS 和Mac OS X 64-bit 的程序都运行在这个环境,也就是说Mac OS X 32-bit 的程序运行在旧的 ...

  3. Objective-C运行时编程 - 方法混写 Method Swizzling

    摘要: 本文描述方法混写对实例.类.父类.不存在的方法等情况处理,属于Objective-C(oc)运行时(runtime)编程范围. 编程环境:Xcode 6.1.1, Yosemite,iOS 8 ...

  4. iOS运行时使用(动态添加方法)

    1 举例  我们实现一个Person类 然后Person 其实是没得对象方法eat:的 下面调用person的eat方法 程序是会奔溃的 那么需要借助运行时动态的添加方法 Person *p = [[ ...

  5. [转] Java运行时动态生成class的方法

    [From] http://www.liaoxuefeng.com/article/0014617596492474eea2227bf04477e83e6d094683e0536000 廖雪峰 / 编 ...

  6. iOS 运行时使用(交换两个方法)

    举例 在创建了如下代码 NSString *str=nil; NSURL *url =[NSURL URLWithString:str]; NSLog(@"%@",url); 但是 ...

  7. jvm入门及理解(四)——运行时数据区(堆+方法区)

    一.堆 定义: Heap,通过new关键字创建的对象,都存放在堆内存中. 特点 线程共享,堆中的对象都存在线程安全的问题 垃圾回收,垃圾回收机制重点区域. jvm内存的划分: JVM内存划分为堆内存和 ...

  8. mxnet运行时遇到问题及解决方法

    1.训练好模型之后,进行预测时出现这种错误: mxnet.::] src/ndarray/ndarray.cc:: Check failed: ,) to.shape=(,) 这种问题的解决方法,在全 ...

  9. loadrunner运行时设置中清空缓存方法

    用函数web_cache_clearup()或run-time settings---browser emulation 把clear  cache on each iteration打勾 W v\] ...

随机推荐

  1. markdown插件学习

    1.Markdown的定义 Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式.它用简洁的语法代替排版,而不像一般我们用的字处理软件Wo ...

  2. dzzoffice应用如何安装

    在dzz应用市场中,进入到每个应用的详细介绍页面.里面有对于每个应用或者主题的安装说明. dzz应用市场:http://dev.dzzoffice.com/index.php?mod=dzzmarke ...

  3. jQuery 关于点击菜单项,使子条目“向上”展开效果的实现

    为什么做了这样一个的功能呢?原因是前一段时间jQuery群里有个朋友想实现这样一个东东,大家都知道jQuery中有现成的slideDown和slideUp方法,但那是向下展开,而这个是一个完全相反的效 ...

  4. 九度 Online Judge 之《剑指 Offer》一书相关题目解答

    前段时间准备华为机试,正好之前看了一遍<剑指 Offer>,就在九度 Online Judge 上刷了书中的题目,使用的语言为 C++:只有3题没做,其他的都做了. 正如 Linus To ...

  5. JavaScript对象(document对象 图片轮播)

    图片轮播: 需要注意的HTML需要img标签,他和input标签一样,是非封闭的标签 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tran ...

  6. Macbook pro内存升级

    http://support.apple.com/kb/HT1270?viewlocale=zh_CN&locale=zh_CN#link1 https://support.apple.com ...

  7. HTML 学习进度备忘

    书签:”HTML 高级教程“及后面的内容尚未学习,另外跳过的内容有待跟进 __________________ 学习资源:W3School. 开始时间:2013.11.20 简述:此网址做为学习教程相 ...

  8. 黑马程序员——Foundation中的OC结构体

    <span style="font-size:14px">------<a target="_blank" href="http:/ ...

  9. SQL SERVER 2008 R2 SP3 发布

    今晚上刚发现,微软很低调啊 下载地址:http://www.microsoft.com/zh-cn/download/details.aspx?id=44271 整合SP3的Express系列版本还没 ...

  10. 200 OK (from cache) 与 304 Not Modified

    解释: 200 OK (from cache)  是浏览器没有跟服务器确认,直接用了浏览器缓存: 304 Not Modified 是浏览器和服务器多确认了一次缓存有效性,再用的缓存. 触发区别: 2 ...