发布自米高 | 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. mysql添加用户和用户权限

    Mysql添加用户使用可以对mysql数据库用户表有操作权限的用户名登陆mysqlinsert into user(Host,User,Password) values('%','name','pas ...

  2. 基于CentOS与VmwareStation10搭建Oracle11G RAC 64集群环境:4.安装Oracle RAC FAQ-4.4.无法图形化安装Grid Infrastructure

    无法图形化安装: [grid@linuxrac1 grid]$ ./runInstaller Starting Oracle Universal Installer... Checking Temp ...

  3. 身份证上的X到底代表什么?

    生活中,无论你是坐火车,还是办理各种手续,都需要用到身份证,它现在已经俨然成为我们生活的非常重要的一部分,但是关于身份证本身,你了解多少呢? 有人会说了,为什么我的身份证上写的有效时间是10年,而一些 ...

  4. [Everyday Mathematics]20150218

    设 $A,B$ 是 $n$ 阶复方阵, 适合 $$\bex A^2B+BA^2=2ABA. \eex$$ 试证: 存在 $k\in\bbZ^+$, 使得 $(AB-BA)^k=0$.

  5. npoi z

    http://blog.csdn.net/fireghost57/article/details/25623143 http://www.cnblogs.com/jiagoushi/archive/2 ...

  6. C# 异步操作 async await 的用法

    1. async与 await 成对出现 async 在方法前使用 ,方法体面面用  await . 2. 使用async 和await定义异步方法不会创建新线程. 3.await 后面一定是一个扫行 ...

  7. 进入appstore中指定的应用

    1.进入appstore中指定的应用 NSString *str = [NSString stringWithFormat:                           @"itms ...

  8. 【转】Bellman_ford算法

    原文链接:http://www.cnblogs.com/Jason-Damon/archive/2012/04/21/2460850.html 摘自百度百科 Bellman-ford算法是求含负权图的 ...

  9. JDK Environment Variable And Change default JDK

    Environment Variable : change(import) /etc/bashrc export JAVA_HOME=/software/jdk1.8.0 export PATH=$J ...

  10. the hard thing about hard things 书摘

    1. from communist to VC 领导力是什么,书后面还举了乔布斯的例子,比如NEXT公司时期就是如此,是什么吸引了那些人在前景不明时还跟随乔布斯?   作者用自己与妻子的相遇说明,不要 ...