zoukankan      html  css  js  c++  java
  • Objective-C运行时编程

    发布自米高 | 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:@"%@ = %@
    ", 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

  • 相关阅读:
    502 IPO 上市
    501 Find Mode in Binary Search Tree
    500 Keyboard Row 键盘行
    498 Diagonal Traverse 对角线遍历
    Django_modelform组件
    Django_RBAC_demo2 升级版权限控制组件
    Django admin组件源码流程
    Django_rbac_demo 权限控制组件框架模型
    Django_重装系统后无法使用 sqlite 数据库报错:com.intellij.execution.ExecutionException: Exception in thread "main" java.lang.ClassNotFoundException: org.sqlite.JDBC
    python_面向对象小试题
  • 原文地址:https://www.cnblogs.com/michaellfx/p/4232205.html
Copyright © 2011-2022 走看看