发布自米高 | 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. “-bash: svn: command not found”

    今天升级了Xcode5.界面更加的清爽了,但是在命令行里SVN也失去了作用了. 当我要更新的时候提示:“-bash: svn: command not found” 解决办法: 1:打开Prefere ...

  2. 自己写了一个类似百度空间自动保存草稿的程序 php+jquery

    可以异步加载mysql中的草稿~,异步更新草稿列表~ 下载地址:http://download.csdn.net/source/3479156 代码: demo.php <?php mysql_ ...

  3. 二.JSP开发过程中遇到的问题及解决

    一.开发环境问题 问题一:Failed to load the JNI shared library 启动Eclipse时弹出“Failed to load the JNI shared librar ...

  4. 【转】SQL Server 数据库内部版本号

    -----------数据库还原或版本升级出现版本错误时可参考. Internal SQL Server Database Version Numbers A database created by ...

  5. 服务器进程为何通常fork()两次

    首先,要了解什么叫僵尸进程,什么叫孤儿进程,以及服务器进程运行所需要的一些条件.两次fork()就是为了解决这些相关的问题而出现的一种编程方法. 孤儿进程 孤儿进程是指父进程在子进程结束之前死亡(re ...

  6. JQuery Mobile移动Web应用开发(1): UI开发工具RID介绍

    工欲善其事,必先利其器. UI工具可以提高我们开发界面的效率,下面对几款工具做个对比: 1. Codiqa,在JQuery Mobile主页能看到这款工具,看到网上这么多人吹捧这个工具,不过是收费的. ...

  7. Windows Azure Platform 系列文章目录

    Windows Azure Platform (一) 云计算的出现 Windows Azure Platform (二) 云计算的分类和服务层次 Windows Azure Platform (三) ...

  8. 谷歌眼镜--UI指南

    1>使用玻璃HTML模板 不是所有的内容都在几行文字来表达.有时候你需要结构化的内容发送到用户的时间轴,或者你需要控制对格式.为了适应这种情况,镜像API提供了一个 HTML 时间表的项目,接受 ...

  9. 第三百三十四天 how can I 坚持

    I give up my dream that day,else,I coming on,the day my heart is die…… 那天,梦已碎,那天,心已死. 晚上看了个电影<奔爱& ...

  10. 文本读写vs二进制读写

    [文本读写vs二进制读写] 在学习C语言文件操作后,我们都会知道打开文件的函数是fopen,也知道它的第二个参数是 标志字符串.其中,如果字符串中出现'b',则表明是以打开二进制(binary)文件, ...