您的位置:首页 > 移动开发 > IOS开发

iOS动态特性初研究(利用JSON动态创建类型和对象)

2013-11-03 10:22 387 查看

1.什么是动态特性?

程序可以访问,检测和修改它本身状态或行为的能力。用我自己的理解,这里的状态和行为,理解成变量,属性和方法,会更加形象一点。

2.与动态特性相关的概念,selector,IMP,Class

Class: 从语法形式上看,和UIButton,NSString一样,是一种类型。

Class被定义为一个指向objc_class的结构体指针。

 


它是指向对象的类结构体的指针,该类结构体含有一个指向其父类类结构的指针,访类方法的链表,该类方法的缓存以及其他必要信息。见下图



除了静态方法来创建对象,还可以使用string来创建,NSClassFromString。

SEL:定义成一个指向objc_selector指针



运行时,会在方法链表中根据SEL查找具体的实现方法IMP。为什么不用函数指针直接调用,而加了一层SEL?我的理解,首先Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取(本人在OC中写过状态机,用函数指针形式写action,但一直报错,只能用selector代替);其次,SEL还可以配合动态方法来使用,例如NSSelectorFromString,performSelector,动态添加方法,并执行。

IMP:就是定义一个函数指针的形式



它包含一个接受消息的对象(self指针),调用方法SEL,以及若干参数,并返回一个id。

 

3. 举例子,如何将JSON直接映射成对象,如何将对象直接映射成DB(coreData原理)

3.1定义该类的属性,方法,生成对象。

用动态方法,获得该对象的属性/变量列表(class_copyPropertyList/class_copyIvarList),遍历获得每个属性的名称(property_getName),然后将JSON转换Dic,用key-value(setvalueForkey,valueForKey)方法,对对象进行赋值,取值操作。

此种方法,抽象出了公用的setter方法(用dictionary給对象赋值),但是缺点是,类型要事先定义。无法动态生成类型。这种例子,网上很多,而且不明白为什么例子中都把property name和attribute值打印出来,至于怎么用,半个字都没提?

 

(上面是最长见的使用方式,有人问我能否不事先定义类型,然后利用JSON来创建类型呢?这个还把我问住了)后来查阅OC runtime guide,发现有动态添加变量的方法(class_addIvar),于是思路由此打开:

3.2首先定义一个空的类

(没有属性,变量,方法),只有一个类名,然后运行时,給该类添加变量(当时没有查到可以动态添加属性的方法,后来发现有,但是要到iOS4.3以后才行),随后用給变量赋值。但是结果让人失望,无法动态添加变量。原因是class_addIvar只能在动态创建类型的时候,添加变量,也就是“class_addIvar"This function may only be called after objc_allocateClassPair and beforeobjc_registerClassPair.Adding
an instance variable to an existing class is notsupported”,而事先定义类是静态创建的类,故无法在runtime时添加变量(http://stackoverflow.com/questions/17888877/objective-c-add-property-in-runtime)

于是,只能放弃事先定义类的方式,转而利用在动态创建类时(objc_allocateClassPair),添加变量 。然后用給变量赋值和取值的方式(object_setInstanceVariable,object_getIvar,注意,无法用key-value的方式操作,这种方法只有静态定义属性后才行),但这种方式,就只能用纯C的方式封装,赋值,取值都要传进obj参数,比较繁琐,没有面向对象那么方便。

结论:3.2中的结论,如果编译前定义类,那么无法用runtime添加变量,这种方法行不通;只有在runtime时,在objc_allocateClassPair和objc_registerClassPair之间用class_addIvar添加变量

 

3.3后来查到有动态添加property的方法 

(class_addProperty),在4.3之后。于是想到一种动态创建类型,并且可以用OC语法的方式访问变量。

首先,动态创建类型,添加变量(这个很重要,因为当我们访问property时,实际上是要对变量操作,如果没有添加变量,那么就是null),注册类型,然后往里动态添加属性,随后就可以象OC一样方便访问属性了 (因为静态类中属性会默认有一个和它同名的变量,对属性操作,实际上是对该变量操作)。

但实际上对该属性赋值后,取值却是null。因为只有在编译前定义的属性才会默认一个变量,property实际上只是提供了setter和getter的方法,至于你要把值存贮在哪里,需要自己设定,所以还需要在class_addProperty方法后,添加property的setter,getter,并在其中确定需要把值保存到哪里,从哪里取值。



getter



setter



这样我们就能用ClassA objA; [objAsetxxx:xxx]; [objA xxx]的方法来访问属性了(本人写了一个简单的实现,但暂时无法上传github,稍后会上传,请各位上传)

 

3.4使用动态创建类,对象,以及ORM的优点,缺点

这个例子有如下几个特点:1.可以动态生成类型 2.可以用OC的方式访问属性。纯粹的“动态”。

当然也有美中不足的地方,首先动态创建对象的类型都是id类型(因为是动态创建,事先没有定义具体类型),视觉上不直观。其次编译过程中,会报warning,因为property是动态添加的,不是编译之前确定的,所以编译器不知道setter,getter方法哪里来的。(当然可以用performSelector来调用就没有warning问题,但是调用方式太繁琐)



但是不影响使用。



结果



结论:3.3的方法比3.2,3.1的方法牛逼,直接动态创建类型和对象,但是牺牲的是code的可读性和可维护性,研究的意义大于实用意义。 

注意:这里需要大家研究的是,如何通过JSON的值,确定动态添加的变量和property的类型,我的思路是,可以容易区分NSString和NSNumber,但是如果确定int,long,float, long long等类型?应该可以通过值的大小范围来确定,例如int -256~255

 

3.5如何将对象映射进DB中,其实原理是一样的,可以运行时,获得类名,属性名,属性类型,值,然后用sqlite3的接口创建表,列,值,类型等等。其实Coredata也是运用了这个动态的原理来实现的。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息