用ios runtime遍历属性性列表 修改了某个属性之后能不能上架

中国领先的IT技术网站
51CTO旗下网站
iOS开发之遍历Model类的属性并完善使用Runtime给Model类赋值
1.要想遍历Model类的属性,首先得通过Runtime来获取该Model类有哪些属性,输出Model的所有属性的值可不像遍历Dictionary和Array那样一个for循环搞定的,下面的方法是通过Runtime来获取Model类的属性字符串,并以数组的形式返回。代码如下:
作者:来源:青玉伏案的博客| 09:33
一、获取Model的实体属性
1.要想遍历Model类的属性,首先得通过Runtime来获取该Model类有哪些属性,输出Model的所有属性的值可不像遍历Dictionary和Array那样一个for循环搞定的,下面的方法是通过Runtime来获取Model类的属性字符串,并以数组的形式返回。代码如下:
&-&(NSArray&*)&allPropertyNames{&&NSMutableArray&*allNames&=&[[NSMutableArray&alloc]&init];&&&unsigned&int&propertyCount&=&0;&&&objc_property_t&*propertys&=&class_copyPropertyList([self&class],&&propertyCount);&&&for&(int&i&=&0;&i&&&propertyC&i&++)&{&&objc_property_t&property&=&propertys[i];&&const&char&*&propertyName&=&property_getName(property);&&[allNames&addObject:[NSString&stringWithUTF8String:propertyName]];&}&&&free(propertys);&&return&allN&}&
2.获取到Model类的属性方法后需要把属性字符串生成get方法,我们可以执行get方法来获取Model属性的值,下方的方法是根据属性字符串来获取属性的getter方法,OC中属性的getter方法的名字和属性的名字是一致的,生成getter方法比较简单,具体代码如下:
#pragma&mark&--&通过字符串来创建该字符串的Setter方法,并返回&-&(SEL)&creatGetterWithPropertyName:&(NSString&*)&propertyName{&&&return&NSSelectorFromString(propertyName);&}&
二、Get方法的执行
接下来要做的是通过Runtime来执行Getter方法,这一块需要通过方法的签名来执行Getter方法。在OC的运行时中要执行的方法需要传入参数或者需要接收返回值时,需要通过方法的签名来调用方法。下面的代码就是创建方法的签名,然后通过签名来获取调用的对象,在下边的方中回调用上述两个方法在通过方法的签名来获取Model属性的值,具体代码如下:
-&(void)&displayCurrentModleProperty{&&&NSArray&*array&=&[self&allPropertyNames];&&&NSMutableString&*resultString&=&[[NSMutableString&alloc]&init];&&for&(int&i&=&0;&i&&&array.&i&++)&{&&&SEL&getSel&=&[self&creatGetterWithPropertyName:array[i]];&&if&([self&respondsToSelector:getSel])&{&&&NSMethodSignature&*signature&=&[self&methodSignatureForSelector:getSel];&&&NSInvocation&*invocation&=&[NSInvocation&invocationWithMethodSignature:signature];&&&[invocation&setTarget:self];&&&[invocation&setSelector:getSel];&&&NSObject&*__unsafe_unretained&returnValue&=&&&&[invocation&invoke];&&&[invocation&getReturnValue:&returnValue];&&[resultString&appendFormat:@&%@\n&,&returnValue];&}&}&NSLog(@&%@&,&resultString);&&}&
执行上述方法就可以输入Model中的属性的值,下面就在main函数中对Model赋完值后调用上述方法输出一下Model的属性值,调用代码如下所示:
BeautifulGirlModel&*beautifulGirl&=&[BeautifulGirlModel&modelWithDictionary:data];&&[beautifulGirl&displayCurrentModleProperty];&
运行结果如下,下面的输出结果是Model中属性的值。
三、Dictionary的Key与Model的属性不同的处理方式
有时候会遇到字典的key和Model的属性不一样的情况,那么如何去解决这个问题呢?最简单的做法是在具体的实体类中去维护一个映射关系方法,通过这个方法我们可以获取相应的的映射关系。
#pragma&返回属性和字典key的映射关系&-(NSDictionary&*)&propertyMapDic{&return&&}&
2.修改一下我们的便利初始化方法,在有映射字典的情况和没有映射字典的情况下调用的方法是不一样的,便利初始化方法的代码如下:
-&(instancetype)initWithDictionary:&(NSDictionary&*)&data{&{&self&=&[super&init];&if&(self)&{&if&([self&propertyMapDic]&==&nil)&{&[self&assginToPropertyWithDictionary:data];&}&else&{&[self&assginToPropertyWithNoMapDictionary:data];&}&}&return&&}&}&
3.接下来就将实现有映射关系要调用的方法,这个方法就是通过映射关系把字典的key转换成与property的名字一样的字典,然后调用之前的赋值方法,具体代码如下:
#pragma&根据映射关系来给Model的属性赋值&-(void)&assginToPropertyWithNoMapDictionary:&(NSDictionary&*)&data{&&NSDictionary&*propertyMapDic&=&[self&propertyMapDic];&&&&NSArray&*dicKey&=&[data&allKeys];&&&NSMutableDictionary&*tempDic&=&[[NSMutableDictionary&alloc]&initWithCapacity:dicKey.count];&&for&(int&i&=&0;&i&&&dicKey.&i&++)&{&NSString&*key&=&dicKey[i];&[tempDic&setObject:data[key]&forKey:propertyMapDic[key]];&}&&[self&assginToPropertyWithDictionary:tempDic];&&}&
4.创建一个BadBoyModel, 并重写propertyMapDic方法,并且在propertyMapDic方法中给出映射关系并返回该映射关系对应的字典。
(1)BadBoyModel的属性如下:
&&&&&&&&#import&&BaseModelObject.h&&&@interface&BadBoyModel&:&BaseModelObject&&@property&(nonatomic,&copy)&NSString&*boy1;&@property&(nonatomic,&copy)&NSString&*boy2;&@property&(nonatomic,&copy)&NSString&*boy3;&@property&(nonatomic,&copy)&NSString&*boy4;&&@end&
(2)重写映射方法,映射字典的key是要转换字典的key, Value是对应Model的属性名。
&&&&&&&&#import&&BadBoyModel.h&&&@implementation&BadBoyModel&&#pragma&返回属性和字典key的映射关系&-(NSDictionary&*)&propertyMapDic{&return&@{@&keyBoy1&:@&boy1&,&@&keyBoy2&:@&boy2&,&@&keyBoy3&:@&boy3&,&@&keyBoy4&:@&boy4&,};&}&&@end&
5.在main函数中进行测试
(1)生成我们的数值字典,字典的key与要赋值Model的属性不同,下面的循环就是要生成测试使用的数据:
&&NSMutableDictionary&*data1&=&[[NSMutableDictionary&alloc]&init];&&&for(int&i&=&1;&i&&=&4;&i&++){&NSString&*key&=&[NSString&stringWithFormat:@&keyBoy%d&,&i];&&NSString&*value&=&[NSString&stringWithFormat:@&我是第%d个坏男孩&,&i];&&[data1&setObject:value&forKey:key];&}&
(2) 实例化Model并输出结果,当然之前的代码也是可以使用的。
BadBoyModel&*badBoyModel&=&[BadBoyModel&modelWithDictionary:data1];&&[badBoyModel&displayCurrentModleProperty];&
运行输出结果如下:
今天博客就到这,至此,Model的基类最基本的方法封装的也就差不多了,根据具体需求可以在添加新的方法
作者:青玉伏案
出处:/ludashi/【责任编辑: TEL:(010)】
大家都在看猜你喜欢
头条头条专题专题专题
24H热文一周话题本月最赞
讲师:5人学习过
讲师:36人学习过
讲师:5人学习过
精选博文论坛热帖下载排行
SQL(结构化查询语言)是数据库系统的通用语言,利用它可以用几乎同样的语句在不同的数据库系统上执行同样的操作,在数据库系统的开发中有着...
订阅51CTO邮刊Function功能(119)
#import &Foundation/Foundation.h&
@interface ObjectRunTime : NSObject
@property(nonatomic,copy)NSString
@property(nonatomic,copy)NSString
@property(nonatomic,assign)BOOL
@property(nonatomic,strong)NSArray
- (NSDictionary *)cf_KeysWithV
//ObjectRunTime.m实现文件
#import "ObjectRunTime.h"
#import &objc/runtime.h&
@implementation ObjectRunTime
- (NSDictionary *)cf_KeysWithValues {
unsigned int count ,i;
objc_property_t *propertyArray = class_copyPropertyList([self class], &count);
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
for (i = 0; i & i++) {
objc_property_t property = propertyArray[i];
NSString *proKey = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
id proValue = [self valueForKey:proKey];
if (proValue) {
[dic setObject:proValue forKey:proKey];
[dic setObject:@"" forKey:proKey];
free(propertyArray);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:99613次
积分:2201
积分:2201
排名:第13699名
原创:99篇
转载:122篇
评论:10条
(23)(7)(8)(11)(6)(2)(3)(4)(5)(4)(6)(4)(2)(2)(2)(7)(4)(4)(9)(3)(6)(2)(4)(9)(14)(7)(7)(4)(5)(7)(7)(1)(4)(5)(28)(4)  在上篇博客《》中介绍了如何使用运行时在实体类的基类中添加给实体类的属性赋值的方法,这个方法的前提是字典的Key必须和实体类的Property Name相同,然后通过运行时来生成和执行Setter方法给Model类的属性赋值。
  通过Runtime来给Model类属性赋值的好处是多多的,它便于代码的后期维护,并且提高了开发效率。当你拿到解析后的字典时你不用一个一个的通过key去把字典的值赋值给相应的Model类的属性,本篇博客中会给出如何去遍历Model中属性的值,并且给出字典的Key和Model的属性名不一样的情况我们该如何负值。
  接下来会在上一个博客代码基础上在Model基类中添加通过Runtime来遍历Model类的属性值。
  一、获取Model的实体属性
  1.要想遍历Model类的属性,首先得通过Runtime来获取该Model类有哪些属性,输出Model的所有属性的值可不像遍历Dictionary和Array那样一个for循环搞定的,下面的方法是通过Runtime来获取Model类的属性字符串,并以数组的形式返回。代码如下:
1 ///通过运行时获取当前对象的所有属性的名称,以数组的形式返回
2 - (NSArray *) allPropertyNames{
///存储所有的属性名称
NSMutableArray *allNames = [[NSMutableArray alloc] init];
///存储属性的个数
unsigned int propertyCount = 0;
///通过运行时获取当前类的属性
objc_property_t *propertys = class_copyPropertyList([self class], &propertyCount);
//把属性放到数组中
for (int i = 0; i & propertyC i ++) {
///取出第一个属性
objc_property_t property = propertys[i];
const char * propertyName = property_getName(property);
[allNames addObject:[NSString stringWithUTF8String:propertyName]];
free(propertys);
return allN
  2.获取到Model类的属性方法后需要把属性字符串生成get方法,我们可以执行get方法来获取Model属性的值,下方的方法是根据属性字符串来获取属性的getter方法,OC中属性的getter方法的名字和属性的名字是一致的,生成getter方法比较简单,具体代码如下:
1 #pragma mark -- 通过字符串来创建该字符串的Setter方法,并返回
2 - (SEL) creatGetterWithPropertyName: (NSString *) propertyName{
//1.返回get方法: oc中的get方法就是属性的本身
return NSSelectorFromString(propertyName);
  二、Get方法的执行
    接下来要做的是通过Runtime来执行Getter方法,这一块需要通过方法的签名来执行Getter方法。在OC的运行时中要执行的方法需要传入参数或者需要接收返回值时,需要通过方法的签名来调用方法。下面的代码就是创建方法的签名,然后通过签名来获取调用的对象,在下边的方中回调用上述两个方法在通过方法的签名来获取Model属性的值,具体代码如下:
1 - (void) displayCurrentModleProperty{
//获取实体类的属性名
NSArray *array = [self allPropertyNames];
//拼接参数
NSMutableString *resultString = [[NSMutableString alloc] init];
for (int i = 0; i & array. i ++) {
//获取get方法
SEL getSel = [self creatGetterWithPropertyName:array[i]];
if ([self respondsToSelector:getSel]) {
//获得类和方法的签名
NSMethodSignature *signature = [self methodSignatureForSelector:getSel];
//从签名获得调用对象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//设置target
[invocation setTarget:self];
//设置selector
[invocation setSelector:getSel];
//接收返回的值
NSObject *__unsafe_unretained returnValue =
[invocation invoke];
//接收返回值
[invocation getReturnValue:&returnValue];
[resultString appendFormat:@"%@\n", returnValue];
NSLog(@"%@", resultString);
  执行上述方法就可以输入Model中的属性的值,下面就在main函数中对Model赋完值后调用上述方法输出一下Model的属性值,调用代码如下所示:
BeautifulGirlModel *beautifulGirl = [BeautifulGirlModel modelWithDictionary:data];
[beautifulGirl displayCurrentModleProperty];
  运行结果如下,下面的输出结果是Model中属性的值。
  三、Dictionary的Key与Model的属性不同的处理方式
    有时候会遇到字典的key和Model的属性不一样的情况,那么如何去解决这个问题呢?最简单的做法是在具体的实体类中去维护一个映射关系方法,通过这个方法我们可以获取相应的的映射关系。
    1.在Model的基类中添加一个返回映射字典的一个方法,然后在子类中进行重写,这个映射方法在基类中返回nil, 如果子类需要重写的话就对这个方法进行重写并返回映射字典。方法如下:
1 #pragma 返回属性和字典key的映射关系
2 -(NSDictionary *) propertyMapDic{
  2.修改一下我们的便利初始化方法,在有映射字典的情况和没有映射字典的情况下调用的方法是不一样的,便利初始化方法的代码如下:
1 - (instancetype)initWithDictionary: (NSDictionary *) data{
self = [super init];
if (self) {
if ([self propertyMapDic] == nil) {
[self assginToPropertyWithDictionary:data];
[self assginToPropertyWithNoMapDictionary:data];
  3.接下来就将实现有映射关系要调用的方法,这个方法就是通过映射关系把字典的key转换成与property的名字一样的字典,然后调用之前的赋值方法,具体代码如下:
1 #pragma 根据映射关系来给Model的属性赋值
2 -(void) assginToPropertyWithNoMapDictionary: (NSDictionary *) data{
///获取字典和Model属性的映射关系
NSDictionary *propertyMapDic = [self propertyMapDic];
///转化成key和property一样的字典,然后调用assginToPropertyWithDictionary方法
NSArray *dicKey = [data allKeys];
NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithCapacity:dicKey.count];
for (int i = 0; i & dicKey. i ++) {
NSString *key = dicKey[i];
[tempDic setObject:data[key] forKey:propertyMapDic[key]];
[self assginToPropertyWithDictionary:tempDic];
  4.创建一个BadBoyModel, 并重写propertyMapDic方法,并且在propertyMapDic方法中给出映射关系并返回该映射关系对应的字典。
    (1)BadBoyModel的属性如下:
BadBoyModel.h
BaseModelProject
Created by Mr.LuDashi on 15/7/24.
Copyright (c) 2015年 ludashi. All rights reserved.
9 #import "BaseModelObject.h"
11 @interface BadBoyModel : BaseModelObject
13 @property (nonatomic, copy) NSString *boy1;
14 @property (nonatomic, copy) NSString *boy2;
15 @property (nonatomic, copy) NSString *boy3;
16 @property (nonatomic, copy) NSString *boy4;
    (2)重写映射方法,映射字典的key是要转换字典的key, Value是对应Model的属性名。
BadBoyModel.m
BaseModelProject
Created by Mr.LuDashi on 15/7/24.
Copyright (c) 2015年 ludashi. All rights reserved.
9 #import "BadBoyModel.h"
11 @implementation BadBoyModel
13 #pragma 返回属性和字典key的映射关系
14 -(NSDictionary *) propertyMapDic{
return @{@"keyBoy1":@"boy1",
@"keyBoy2":@"boy2",
@"keyBoy3":@"boy3",
@"keyBoy4":@"boy4",};
    5.在main函数中进行测试
      (1)、生成我们的数值字典,字典的key与要赋值Model的属性不同,下面的循环就是要生成测试使用的数据:
//生成Dic的Key与Model的属性不一样的字典。
NSMutableDictionary *data1 = [[NSMutableDictionary alloc] init];
//创建测试适用的字典
for(int i = 1; i &= 4; i ++){
NSString *key = [NSString stringWithFormat:@"keyBoy%d", i];
NSString *value = [NSString stringWithFormat:@"我是第%d个坏男孩", i];
[data1 setObject:value forKey:key];
      (2) 实例化Model并输出结果,当然之前的代码也是可以使用的。
BadBoyModel *badBoyModel = [BadBoyModel modelWithDictionary:data1];
[badBoyModel displayCurrentModleProperty];
    运行输出结果如下:
    今天博客就到这,至此,Model的基类最基本的方法封装的也就差不多了,根据具体需求可以在添加新的方法
阅读(...) 评论()runtime-属性与变量 - 简书
<div class="fixed-btn note-fixed-download" data-toggle="popover" data-placement="left" data-html="true" data-trigger="hover" data-content=''>
写了98594字,被2192人关注,获得了1391个喜欢
runtime-属性与变量
runtime简称运行时,就是在程序运行时的一些机制,在iOS开发中runtime的特性使得oc这门语言具有独特的魅力。对于C、C++来说,在程序编译运行时,类对象能调用哪些方法,能进行什么操作,都被决定好了。而runtime机制让oc能在运行时动态的创建类、黑盒测试、扩展属性等等,极大的提高了语言的灵活性。今天结合runtime的一些机制来谈谈oc的属性和变量。(这是我关于runtime机制的开篇,若文中提及的某些知识点有什么不同的意见,欢迎在评论中与我一同探讨)
property和ivar
首先要确定的是,属性(property)和成员变量(ivar)它们是不同的东西,在oc中它们的区别如下:
成员变量成员变量通常在类声明@interface或者类实现@implementation后面的大括号中声明的变量,默认修饰为@protected保护类型(文件外不能访问),除此之外还有@public公共类型、@private私有类型和@package包内访问类型
@interface Person
///& 所有可视范围都能访问
NSString *
NSString *
只有本类能够访问
NSString * personalW
@protected
///& 本类和子类都能访问
NSString * housesN
框架内视为public,框架外为private
NSString * familyW
属性相比起变量,在编译期间,编译器做了很多工作,包括这些:1、使用@synthesize生成属性对应的ivar,通常ivar命名为下划线+属性名2、生成setter方法来设置ivar3、生成getter方法来获取ivar
从某个意义上来说,属性是对成员变量的封装,在其基础上添加了setter和getter两种方法使变量更符合面向对象的需求。(对于不明白为什么要存在setter和getter的开发者们可以看这篇文章)
属性的内存结构与@synthesize
在我之前那篇文章中,我稍微提到过类的内存结构,这里要更为深入的了解声明属性然后运行后内存结果发生的改变,这里我们会发现@synthesize具体做的事情。现在我的Person类的代码如下:
@interface Person: NSObject {
NSString * _
NSString * _
@property(nonatomic, copy) NSString *
@property(nonatomic, copy) NSString *
@implementation Person
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%p, %p, %p, %p, %p, %p, %p", self, &_name, &_sex, &_ch, _abc, _efg);
问题一:成员变量的地址偏移
虽然OC作为一门动态语言有自己的特性,但是从类结构的角度来说,和其他语言的差别并不会很大。按照类结构的角度来看,类中的成员变量的地址都是基于类对象自身地址进行偏移的,那么这几个变量的地址应该是依次增加0x8(32位系统上则是0x4)。上面代码的日志输出如下:
0x7fb649c0c9b0, 0x7fb649c0c9c0, 0x7fb649c0c9c8, 0x7fb649c0c9d0, 0x7fb649c0c9d8, 0x7fb649c0c9e0
可以看到后面三个地址确实相差为0x8,但是在类对象和第一个成员变量之间相差的地址是0x10。这是为什么呢?在的相关代码中,我们可以找到Class类型的定义
typedef struct objc_class *C
struct objc_class {
······
Class表示OC中的类结构,从这段代码中我们可以看到它是结构体objc_class的指针类型,在这个结构体中有一个isa指针变量。而这个多出的指针变量也不难解释了为什么上面的输出中出现0x10的偏移——两个地址之间相差了一个isa。更为详细的内容,将会在之后其他的runtime文章中具体讲述。
问题二:地址偏移的计算方式是什么?
指针在64位系统占用8bit这个没有任何问题,但是char类型只用到一bit,但是这里同样偏移了8位,是否也是按照结构体的地址偏移计算的?这里要提到一个给类添加变量的函数class_addIvar(const char *, NSUInteger *, NSUInteger *),其中最后一个参数用来表示变量的内存地址对其方式。苹果对这个参数解释是:
The instance variable's minimum alignment in bytes is 1&&align. The minimum alignment of an instance variable depends on the ivar's type and the machine architecture. For variables of any pointer type, pass log2(sizeof(pointer_type)).
这里说了alignment是变量以字节为单位的最小对齐方式,但是却
没有细说怎样对齐。而在中有地址偏移计算的代码,我们可以通过这些代码了解的更清楚:
uint32_t offset = cls-&unalignedInstanceSize();
uint32_t alignMask = (1&&alignment)-1;
offset = (offset + alignMask) & ~alignM
简单来说就是苹果规定了某个变量它的偏移默认为1 && alignment,而在上下文中这个值为指针长度。因此,OC中类结构地址的偏移计算与结构体还是有不同的,只要是小于8bit长度的地址,统一归为8bit偏移。
问题三:属性的变量是怎么存放的?
前面我们说过了使用@property声明的属性在编译阶段会自动生成一个以下划线开头的ivar并且绑定setter和getter方法,所以我们可以在类文件中使用_property的方式访问变量。那么根据上面的地址偏移的输出,属性生成的变量实际上是跟在成员变量的后面的,那么这是怎么实现的?在问题二中我提到了一个runtime的函数class_addIvar(),在Xcode中函数的描述如下:
* @note This function may only be called after objc_allocateClassPair and before objc_registerClassPair.
Adding an instance variable to an existing class is not supported.
* @note The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.
* @note The instance variable's minimum alignment in bytes is 1&&align. The minimum alignment of an instance
variable depends on the ivar's type and the machine architecture.
For variables of any pointer type, pass log2(sizeof(pointer_type)).
在编译器编译代码的期间,对类的操作包括了创建类内存、添加变量、属性、方法列表……操作,在完成这些操作之后,还需要注册类类型后才能够使用。而class_addIvar()函数在注册前使用,为类添加成员变量并且加入变量列表当中。根据这个函数,我们推测@synthesize在编译期间通过了这个函数为属性添加实例变量,并且存放起来。如果我们的猜测是正确的,那么我们可以在实例变量的列表中找到这些属性对应的变量。对于这个问题,runtime同样提供了方法给我们进行测试。Ivar * class_copyIvarList(Class, unsigned int *)返回类结构中的变量列表,我们可以通过下面的代码获取Person所有的变量并且输出变量名:
unsigned int ivarC
Ivar * ivars = class_copyIvarList([Person class], &ivarCount);
for (int idx = 0; idx & ivarC idx++) {
Ivar ivar = ivars[idx];
NSLog(@"%s", ivar_getName(ivar));
free(ivars);
上面Person类的实例变量列表输出结果如下:
21:59:49.580 LXDCodingDemo[] _omg
21:59:49.581 LXDCodingDemo[] _name
21:59:49.581 LXDCodingDemo[] _ch
21:59:49.581 LXDCodingDemo[] sct
21:59:49.581 LXDCodingDemo[] _sex
21:59:49.581 LXDCodingDemo[] _copying
21:59:49.581 LXDCodingDemo[] _egf
21:59:49.581 LXDCodingDemo[] _hij
我们可以看到@synthesize确实调用了这个方法,其绑定属性与变量内存的方式是通过class_addIvar()函数来实现的。
问题四:@synthesize到底做了什么?
这个问题可能有些匪夷所思,从上面的代码跟问题结合来看,毫无疑问@synthesize为变量生成并且绑定了变量内存。我们在声明属性的时候,比如Person类中的abc属性,那么编译器会在编译期间帮我们自动生成@synthesize abc = _这句代码,这意味着我们可以自己来写出这句。那么假如我们把属性和已存在的成员变量进行绑定呢?比如写成@synthesize abc = _name,那么修改之后再次输出地址会变成怎样?
@implementation Person
@synthesize abc = _
///& 自定义绑定属性
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%p, %p, %p, %p", &_name, &_sex, &_ch, &_efg);
原先的代码在添加了自定义绑定的这句代码后会报错,由于我们给abc属性绑定了_name的内存地址,那么编译器就不会生成_abc变量,所以在类中找不到这个变量的存在。在创建Person的实例后控制台输出的地址信息没有发生变化,依旧是相差0x8
0x7ff92b45a438, 0x7ff92b45a440, 0x7ff92b45a448, 0x7ff92b45a458
为了检测abc和_name的关系,我在main函数中加入了这段代码:
Person * p = [Person new];
p.abc = @"123";
NSLog(@"%@, %@", p.abc, p-&_name);
输出的结果是abc跟_name的结果是一样的。通过这个小?,我们不难发现@synthesize在为属性添加变量内存的时候,会先搜索是否已经存在同名的实例变量,如果存在,将生成getter和setter方法来访问这块内存地址。否则生成新的成员变量地址,然后再绑定setter和getter。因此@synthesize在添加变量的工作中不仅仅是简单的class_addIvar(),还有遍历变量列表的过程。
@synthesize与@dynamic
跟黑白对立一样,有了@synthesize这样的存在,必然也会有相反的机制,在OC中我们可以使用@dynamic propertyName的方式阻止编译器为属性完成变量捆绑和setter、getter生成的工作,然后交由我们在运行时再去生成这些方法。这些将会在runtime的消息篇中讲解。
问题五:@synthesize如何判断属性的类型?
假如我们在上面自定义的绑定代码中绑定的不是_name而是_ch呢?那么编译器会报错,这是由于类型检测的结果。但是编译器在默认生成属性对应的变量内存的时候,又是怎么判断属性的类型的?另外,属性还拥有着copy、strong、weak···更多的属性类型,这关乎setter方法的实现,@synthesize又是怎么区分的?在Xcode中有个并不常用的关键字@encode,这个关键字使用后返回描述类型的编码,我在main函数中添加了这么一段代码以及控制台的输出结果:
NSLog(@"%s, %s, %s", @encode(Person), @encode(CGRect), @encode(NSInteger));
{Person=#@@c}, {CGRect={CGPoint=dd}{CGSize=dd}}, q
看起来有些混乱,在苹果官方文档中提到了编译器用C字符来表示所有的OC类型,而使用@encode(type)可以获取这个类型的编码,这些编码的对应关系在中可以看到。
从上面的输出中我们看到了Person对应的编码是#@@c,其中#表示对象,后面跟着的分别表示id、id、char,结合类文件来看,这里分别表示_name、_sex、_ch。那么这也就可以看出@synthesize是怎么判断出属性绑定的变量类型了。而在class_addIvar()函数中接受一个const char *类型的参数用来表示实例变量的属性类型、变量类型等,这时候@synthesize就能将获取的类型编码传入然后生成对应的变量。
另外,对于属性类型的判断又是怎么样的呢?同样的,苹果在runtime中提供给我们property_getAttributes()来获取一个对象的类型属性,这些类型属性也同样采用了@encode类似的一套类型编码,这些类型编码的标准表同样可以在中找到。如果你喜欢看各种开源框架的代码,那么最近突起的YYModel中你可以看到作者对于类型编码的大量应用:
YYModel的类型编码
不能实践的理论都是废话 —— 沃德天·毫率
上面我总结出了很多头头是道的理论,但是如果不能使用并没有什么卵用。在我们开发中,数据持久化是避不可免的业务实现,由于博主公司项目都不大,也没有太多的数据需要存储,因此正常来说博主都是直接使用NSCoding提供的数据归档进行的持久化。那么就经常出现这样的代码:
模型的数据归档
首先在模型数据还没有那么多的时候,这么写并不会出现什么问题。当模型的数据越来越多,直接这么写就可能导致:
1、数据过多导致归档操作中字符串可能对应不上,导致存取失败
2、工作量加大
上面我们说到过runtime中存在class_copyIvarList()函数来获取一个类的所有实例变量,对于属性同样存在着class_copyPropertyList()函数。因此,我们可以通过这个函数来遍历获取属性以及属性名称,然后实现类似单例宏定义的一键归档宏定义。核心代码如下:
unsigned int propertyC
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
for (int idx = 0; idx & propertyC idx++) {
objc_property_t property = properties[idx];
NSLog(@"\n--name: %s\n--attributes: %s", property_getName(property), property_getAttributes(property));
free(properties);
控制台输出属性的相关信息:
--name: abc
--attributes: T@"NSString",C,N,V_abc
--name: efg
--attributes: T@"NSString",C,N,V_efg
--name: hij
--attributes: T@"NSString",C,N,V_hij
通过runtime来遍历类属性然后进行归档和反归档的过程中都有这么一段遍历属性的过程,那么可以定义一个LXDCodingHandler的block用来存储遍历中对objc_property_t相关属性的处理并传入这个遍历中:
typedef void(^LXDCodingHandler)(objc_property_t property, NSString * propertyName);
相关代码我已经完成了封装,实现了一行代码对模型进行序列化操作。
转载请注明作者和地址:
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
如果你是程序员,或者有一颗喜欢写程序的心,喜欢分享技术干货、项目经验、程序员日常囧事等等,欢迎投稿《程序员》专题。
专题主编:小...
· 263986人关注
【最新投稿方式】
为了保证专题文章的质量,暂时关闭投稿申请,改为【私信文章链接】给本专题的管理员 (花前月下) (判若...
· 7946人关注
学习从点滴开始 ! (ps: 由于专题内文章越来越多, 部分重复知识点的投稿文章就不再收录, 拒绝部分朋友的投稿并非是你的文章写得不好...
· 6107人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:

我要回帖

更多关于 淘宝网几款能不能上架 的文章

 

随机推荐