分析Runtime的属性Property

Posted xyq-208910

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分析Runtime的属性Property相关的知识,希望对你有一定的参考价值。

一、介绍

在OC中我们可以给任意的一个类以@property的格式声明属性,当然对于这个属性也会采用某一些属性关键字进行修饰,那么属性的真正的面目是啥样子的呢?其实,runtime源码中可以看到,property是一个结构,如下所示,只不过苹果为这个结构体另外定义了一个结构体指针。

//属性结构体指针
typedef struct objc_property *objc_property_t;

 

二、函数

正如我们所知,MJExtension是一个非常流行的json解析框架,其内部对对象的每一个属性进行解析的方式其实就是通过runtime来实现的。runtime源码中提供了属性的相关API函数。通过这些函数,我们可以轻松的得到每一个属性名和类型以及特性,相关函数如下:

//获取一个属性
objc_property_t _Nullable class_getProperty(Class _Nullable cls, const char * _Nonnull name);

//添加一个属性
class_addProperty(Class _Nullable cls, const char * _Nonnull name, const objc_property_attribute_t * _Nullable attributes,unsigned int attributeCount);

//替换一个属性
class_replaceProperty(Class _Nullable cls, const char * _Nonnull name, const objc_property_attribute_t * _Nullable attributes,unsigned int attributeCount)

//获取属性数组
objc_property_t _Nonnull * _Nullable class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount);

//获取属性名称
const char * _Nonnull property_getName(objc_property_t _Nonnull property) ;

//获取属性描述名称
const char * _Nullable property_getAttributes(objc_property_t _Nonnull property);

//获取属性描述数组
objc_property_attribute_t * _Nullable property_copyAttributeList(objc_property_t _Nonnull property,unsigned int * _Nullable outCount);

//通过属性描述的name获取属性描述的value
property_copyAttributeValue(objc_property_t _Nonnull property,const char * _Nonnull attributeName); 

//属性描述结构体
typedef struct {
    const char * _Nonnull name;           /**< The name of the attribute */
    const char * _Nonnull value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

 

三、案例

了解了基本函数后,现在来上手,获取并打印看看property到底是个啥样,代码如下:

//定义一个对象,声明各种属性property
@interface SubObject : NSObject
@property (nonatomic,   copy) NSString *name;
@property (nonatomic,   copy) void (^Block)(void);
@property (nonatomic,   weak) id<NSObject> delegate;
@property (nonatomic, assign) int      age;
@property (nonatomic, assign) float    height;
@property (nonatomic, assign) double   weight;
@property (nonatomic, strong) NSNumber *num;
@property (nonatomic, strong) NSDate   *date;
@property (nonatomic, strong) NSData   *data;
@property (nonatomic, strong) NSArray  *array;
@property (nonatomic, strong) NSDictionary *dic;
@property (nonatomic, strong) MyObject *myObject;
@end
//获取属性列表
unsigned int count;
objc_property_t *properties = class_copyPropertyList([subObject class], &count);
for (int i=0; i<count; i++) {
        
      //获取属性
      objc_property_t property = properties[i];
        
      //属性名称
      const char *propertyName = property_getName(property);
      NSLog(@"------propertyName = %s-------",propertyName);
        
      //属性描述名称
      const char * property_attr = property_getAttributes(property);
      NSLog(@"------property_attr = %s-------",property_attr);
        
      //属性类型
      const char *propertyType = property_copyAttributeValue(property, "T");
      NSLog(@"------propertyType = %s-------",propertyType);
        
      //属性变量名称
      const char *property_Value = property_copyAttributeValue(property, "V");
      NSLog(@"------property_Value = %s-------",property_Value);
        
      //属性描述列表
      unsigned int outCount;
      objc_property_attribute_t *attributeList = property_copyAttributeList(property, &outCount);
      for (int j=0; j<outCount; j++) {
          objc_property_attribute_t attribute = attributeList[j];
          NSLog(@"property_attribute_t_name:%s-----property_attribute_t_value:%s",attribute.name,attribute.value);
      }
      NSLog(@"");
}
2019-11-10 19:18:10.335830+0800 运行时[4530:256535] ------propertyName = name-------
2019-11-10 19:18:10.336009+0800 运行时[4530:256535] ------property_attr = T@"NSString",C,N,V_name-------
2019-11-10 19:18:10.336152+0800 运行时[4530:256535] ------propertyType = @"NSString"-------
2019-11-10 19:18:10.336261+0800 运行时[4530:256535] ------property_Value = _name-------
2019-11-10 19:18:10.336405+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:@"NSString"
2019-11-10 19:18:10.336532+0800 运行时[4530:256535] property_attribute_t_name:C-----property_attribute_t_value:
2019-11-10 19:18:10.336648+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.336770+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_name
2019-11-10 19:18:10.336877+0800 运行时[4530:256535] 
2019-11-10 19:18:10.336974+0800 运行时[4530:256535] ------propertyName = Block-------
2019-11-10 19:18:10.337256+0800 运行时[4530:256535] ------property_attr = T@?,C,N,V_Block-------
2019-11-10 19:18:10.337568+0800 运行时[4530:256535] ------propertyType = @?-------
2019-11-10 19:18:10.337972+0800 运行时[4530:256535] ------property_Value = _Block-------
2019-11-10 19:18:10.338296+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:@?
2019-11-10 19:18:10.338638+0800 运行时[4530:256535] property_attribute_t_name:C-----property_attribute_t_value:
2019-11-10 19:18:10.341894+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.342363+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_Block
2019-11-10 19:18:10.342769+0800 运行时[4530:256535] 
2019-11-10 19:18:10.343143+0800 运行时[4530:256535] ------propertyName = delegate-------
2019-11-10 19:18:10.343664+0800 运行时[4530:256535] ------property_attr = T@"<NSObject>",W,N,V_delegate-------
2019-11-10 19:18:10.343779+0800 运行时[4530:256535] ------propertyType = @"<NSObject>"-------
2019-11-10 19:18:10.344314+0800 运行时[4530:256535] ------property_Value = _delegate-------
2019-11-10 19:18:10.344746+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:@"<NSObject>"
2019-11-10 19:18:10.345172+0800 运行时[4530:256535] property_attribute_t_name:W-----property_attribute_t_value:
2019-11-10 19:18:10.345485+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.345773+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_delegate
2019-11-10 19:18:10.346062+0800 运行时[4530:256535] 
2019-11-10 19:18:10.346475+0800 运行时[4530:256535] ------propertyName = age-------
2019-11-10 19:18:10.346859+0800 运行时[4530:256535] ------property_attr = Ti,N,V_age-------
2019-11-10 19:18:10.347278+0800 运行时[4530:256535] ------propertyType = i-------
2019-11-10 19:18:10.347784+0800 运行时[4530:256535] ------property_Value = _age-------
2019-11-10 19:18:10.348131+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:i
2019-11-10 19:18:10.348551+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.348980+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_age
2019-11-10 19:18:10.349373+0800 运行时[4530:256535] 
2019-11-10 19:18:10.349737+0800 运行时[4530:256535] ------propertyName = height-------
2019-11-10 19:18:10.350087+0800 运行时[4530:256535] ------property_attr = Tf,N,V_height-------
2019-11-10 19:18:10.350393+0800 运行时[4530:256535] ------propertyType = f-------
2019-11-10 19:18:10.350763+0800 运行时[4530:256535] ------property_Value = _height-------
2019-11-10 19:18:10.351191+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:f
2019-11-10 19:18:10.351596+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.351971+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_height
2019-11-10 19:18:10.352397+0800 运行时[4530:256535] 
2019-11-10 19:18:10.352694+0800 运行时[4530:256535] ------propertyName = weight-------
2019-11-10 19:18:10.353102+0800 运行时[4530:256535] ------property_attr = Td,N,V_weight-------
2019-11-10 19:18:10.353529+0800 运行时[4530:256535] ------propertyType = d-------
2019-11-10 19:18:10.353791+0800 运行时[4530:256535] ------property_Value = _weight-------
2019-11-10 19:18:10.354096+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:d
2019-11-10 19:18:10.354411+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.354693+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_weight
2019-11-10 19:18:10.355023+0800 运行时[4530:256535] 
2019-11-10 19:18:10.355303+0800 运行时[4530:256535] ------propertyName = num-------
2019-11-10 19:18:10.355567+0800 运行时[4530:256535] ------property_attr = T@"NSNumber",&,N,V_num-------
2019-11-10 19:18:10.355887+0800 运行时[4530:256535] ------propertyType = @"NSNumber"-------
2019-11-10 19:18:10.356349+0800 运行时[4530:256535] ------property_Value = _num-------
2019-11-10 19:18:10.357784+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:@"NSNumber"
2019-11-10 19:18:10.358246+0800 运行时[4530:256535] property_attribute_t_name:&-----property_attribute_t_value:
2019-11-10 19:18:10.358968+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.359325+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_num
2019-11-10 19:18:10.359998+0800 运行时[4530:256535] 
2019-11-10 19:18:10.360152+0800 运行时[4530:256535] ------propertyName = date-------
2019-11-10 19:18:10.360572+0800 运行时[4530:256535] ------property_attr = T@"NSDate",&,N,V_date-------
2019-11-10 19:18:10.360834+0800 运行时[4530:256535] ------propertyType = @"NSDate"-------
2019-11-10 19:18:10.361263+0800 运行时[4530:256535] ------property_Value = _date-------
2019-11-10 19:18:10.361579+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:@"NSDate"
2019-11-10 19:18:10.361951+0800 运行时[4530:256535] property_attribute_t_name:&-----property_attribute_t_value:
2019-11-10 19:18:10.362362+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.362838+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_date
2019-11-10 19:18:10.363256+0800 运行时[4530:256535] 
2019-11-10 19:18:10.363500+0800 运行时[4530:256535] ------propertyName = data-------
2019-11-10 19:18:10.363788+0800 运行时[4530:256535] ------property_attr = T@"NSData",&,N,V_data-------
2019-11-10 19:18:10.364093+0800 运行时[4530:256535] ------propertyType = @"NSData"-------
2019-11-10 19:18:10.364430+0800 运行时[4530:256535] ------property_Value = _data-------
2019-11-10 19:18:10.364925+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:@"NSData"
2019-11-10 19:18:10.365110+0800 运行时[4530:256535] property_attribute_t_name:&-----property_attribute_t_value:
2019-11-10 19:18:10.365525+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.365928+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_data
2019-11-10 19:18:10.366541+0800 运行时[4530:256535] 
2019-11-10 19:18:10.366648+0800 运行时[4530:256535] ------propertyName = array-------
2019-11-10 19:18:10.366929+0800 运行时[4530:256535] ------property_attr = T@"NSArray",&,N,V_array-------
2019-11-10 19:18:10.367389+0800 运行时[4530:256535] ------propertyType = @"NSArray"-------
2019-11-10 19:18:10.367686+0800 运行时[4530:256535] ------property_Value = _array-------
2019-11-10 19:18:10.368112+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:@"NSArray"
2019-11-10 19:18:10.368357+0800 运行时[4530:256535] property_attribute_t_name:&-----property_attribute_t_value:
2019-11-10 19:18:10.368664+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.368905+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_array
2019-11-10 19:18:10.369409+0800 运行时[4530:256535] 
2019-11-10 19:18:10.369565+0800 运行时[4530:256535] ------propertyName = dic-------
2019-11-10 19:18:10.369810+0800 运行时[4530:256535] ------property_attr = T@"NSDictionary",&,N,V_dic-------
2019-11-10 19:18:10.370092+0800 运行时[4530:256535] ------propertyType = @"NSDictionary"-------
2019-11-10 19:18:10.370366+0800 运行时[4530:256535] ------property_Value = _dic-------
2019-11-10 19:18:10.370706+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:@"NSDictionary"
2019-11-10 19:18:10.371128+0800 运行时[4530:256535] property_attribute_t_name:&-----property_attribute_t_value:
2019-11-10 19:18:10.371537+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.371911+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_dic
2019-11-10 19:18:10.372340+0800 运行时[4530:256535] 
2019-11-10 19:18:10.372762+0800 运行时[4530:256535] ------propertyName = myObject-------
2019-11-10 19:18:10.373469+0800 运行时[4530:256535] ------property_attr = T@"MyObject",&,N,V_myObject-------
2019-11-10 19:18:10.373751+0800 运行时[4530:256535] ------propertyType = @"MyObject"-------
2019-11-10 19:18:10.374167+0800 运行时[4530:256535] ------property_Value = _myObject-------
2019-11-10 19:18:10.374515+0800 运行时[4530:256535] property_attribute_t_name:T-----property_attribute_t_value:@"MyObject"
2019-11-10 19:18:10.374742+0800 运行时[4530:256535] property_attribute_t_name:&-----property_attribute_t_value:
2019-11-10 19:18:10.375014+0800 运行时[4530:256535] property_attribute_t_name:N-----property_attribute_t_value:
2019-11-10 19:18:10.375595+0800 运行时[4530:256535] property_attribute_t_name:V-----property_attribute_t_value:_myObject
2019-11-10 19:18:10.375718+0800 运行时[4530:256535] 

 

四、分析

看到上述打印,发现一些特性,可以获取到属性名称、属性类型,属性原子性、属性引用情况等,这些基本都在属性描述property_attr中,并使用一些特别的字符表示,例如T、@,&,N,V等,每一个属性描述都是以字符“T”开头,以字符“V下划线变量名 V_xxx”结尾 (结尾不固定,对于其他属性)。

例如属性name的属性描述打印:

//T:  Type(@encode) 属性类型  
//@:  对象类型, NSString类型
//C: Copy  拷贝特性
//N: nonatomic 非原子性,默认为atomic,atomic为空 
//V:variable 变量 _name , 使用@property修饰的属性系统默认@synthesize定义下划线变量,_name
property_attr = T@"NSString",C,N,V_name

例如属性age的属性描述打印:

//T:  Type(@encode) 属性类型  
//i:  基本数据类型, int类型//N: nonatomic 非原子性,默认为atomic,atomic为空 
//V:variable 变量 _age , 使用@property修饰的属性系统默认@synthesize定义下划线变量,_age
property_attr = Ti,N,V_age-------

例如属性myObject的属性描述打印:

//T:  Type(@encode) 属性类型  
//@:  对象类型, 自定义类MyObject类型
//&:  引用特性, 强引用
//N: nonatomic 非原子性,默认为atomic,atomic为空 
//V:variable 变量 _myObject-- , 使用@property修饰的属性系统默认@synthesize定义下划线变量,_myObject--
property_attr = T@"MyObject",&,N,V_myObject-------

就不一一列举了,基本属性特性大概如下,可能不完整:

//基本字符表示
T  是固定开头,表示类型,后跟类型 @、i、f、d、#等
&  代表强引用
C  代表copy
R  代表readOnly属性,readwrite时为空
W  代表weak,assign为空,默认为assign。
N  区分的nonatomic和atomic,默认为atomic,atomic为空,’N’代表是nonatomic
D  @dynamic 动态特性,动态绑定,需要手动生成setter和getter方法
V_exprice 固定结尾,V代表变量,后面紧跟着的是成员变量名,代表这个property的成员变量名为_exprice。

属性类型的表示字符特性编码苹果官方给了一套参考:特性编码解析大全

//编码值   含意
//c       代表char类型
//i       代表int类型
//s       代表short类型
//l       代表long类型,在64位处理器上也是按照32位处理
//q       代表long long类型
//C       代表unsigned char类型
//I       代表unsigned int类型
//S       代表unsigned short类型
//L       代表unsigned long类型
//Q       代表unsigned long long类型
//f       代表float类型
//d       代表double类型
//B       代表C++中的bool或者C99中的_Bool
//v       代表void类型
//*       代表char *类型
//@       代表对象类型
//#       代表类对象 (Class)
//:       代表方法selector (SEL)
//[array type]  代表array
//{name=type…}  代表struct结构体
//(name=type…)  代表union共同体
//bnum   A bit field of num bits,代表位字段
//^type  A pointer to type,代表指针类型
//?      An unknown type (among other things, this code is used for function pointers),代表未知类型,例如block

 

四、扩展

类的属性声明

//类拥有一个属性
NS_ASSUME_NONNULL_BEGIN
@interface MyObject : NSObject
@property (nonatomic,   copy) NSString *name;
@end

1、获取

-(void)printPropertyWithObj:(MyObject *)myObject{
  unsigned int count;
  objc_property_t *properties = class_copyPropertyList([myObject class], &count);
  for (int i=0; i<count; i++) {
        
    //获取属性
    objc_property_t property = properties[i];
        
    //属性名称
    const char *propertyName = property_getName(property);
    NSLog(@"------propertyName = %s-------",propertyName);
        
    //属性变量
    char *property_Value = property_copyAttributeValue(property, "V");
    NSLog(@"------property_Value = %s-------",property_Value);
        
    //属性类型
    char *property_Type = property_copyAttributeValue(property, "T");
    NSLog(@"------property_Type = %s-------",property_Type);    
  }
}
2019-11-10 21:57:24.258232+0800 运行时[6007:365556] ------propertyName = name-------
2019-11-10 21:57:24.258412+0800 运行时[6007:365556] ------property_Value = _name-------
2019-11-10 21:57:24.258541+0800 运行时[6007:365556] ------property_Type = @"NSString"-------

2、添加

//添加属性
//* @param cls The class to modify.
//* @param name The name of the property.
//* @param attributes An array of property attributes.
//* @param attributeCount The number of attributes in e attributes.
//class_addProperty(Classcls, const char *name, const objc_property_attribute_t *attributes,unsigned int attributeCount)
const char * propertyName = "age";
objc_property_attribute_t type = { "T", "i" }; //int
objc_property_attribute_t ownership = { "N" }; //nonatomic
objc_property_attribute_t variable  = { "V", "_age" }; //_age
objc_property_attribute_t attrs[]   = { type, ownership, variable };
BOOL addSuccess = class_addProperty([myObject class], propertyName, attrs, 3);
if (addSuccess) {
   [self printPropertyWithObj:myObject];
}
2019-11-10 22:00:12.120742+0800 运行时[6064:369407] 默认属性如下:
2019-11-10 22:00:12.120941+0800 运行时[6064:369407] ------propertyName = name-------
2019-11-10 22:00:12.121073+0800 运行时[6064:369407] ------property_Value = _name-------
2019-11-10 22:00:12.121199+0800 运行时[6064:369407] ------property_Type = @"NSString"-------
2019-11-10 22:00:12.121295+0800 运行时[6064:369407] 
2019-11-10 22:00:12.121405+0800 运行时[6064:369407] 添加属性如下:
2019-11-10 22:00:12.121617+0800 运行时[6064:369407] ------propertyName = age-------
2019-11-10 22:00:12.121731+0800 运行时[6064:369407] ------property_Value = _age-------
2019-11-10 22:00:12.121886+0800 运行时[6064:369407] ------property_Type = i-------
2019-11-10 22:00:12.122279+0800 运行时[6064:369407] ------propertyName = name-------
2019-11-10 22:00:12.122780+0800 运行时[6064:369407] ------property_Value = _name-------
2019-11-10 22:00:12.123222+0800 运行时[6064:369407] ------property_Type = @"NSString"-------

3、替换

//替换属性【注意:无法改变属性名称,只是修改生成的下划线变量,_variable】
//* @param cls The class to modify.
//* @param name The name of the property.
//* @param attributes An array of property attributes.
//* @param attributeCount The number of attributes in e attributes.
//class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t * _Nullable attributes, unsigned int attributeCount)
const char * propertyName = "name";
objc_property_attribute_t type = { "T", "@"NSString"" };//NSString
objc_property_attribute_t ownership = { "C", "N" }; // copy、nonatomic
objc_property_attribute_t variable  = { "V", "_newName" }; //_newName
objc_property_attribute_t attrs[]   = { type, ownership, variable };
class_replaceProperty([myObject class], propertyName, attrs, 3);
[self printPropertyWithObj:myObject];
2019-11-10 22:03:52.448875+0800 运行时[6111:373162] 默认属性如下:
2019-11-10 22:03:52.450451+0800 运行时[6111:373162] ------propertyName = name-------
2019-11-10 22:03:52.452714+0800 运行时[6111:373162] ------property_Value = _name-------
2019-11-10 22:03:52.453001+0800 运行时[6111:373162] ------property_Type = @"NSString"-------
2019-11-10 22:03:52.453125+0800 运行时[6111:373162] 
2019-11-10 22:03:52.458433+0800 运行时[6111:373162] 替换属性如下:
2019-11-10 22:03:52.458658+0800 运行时[6111:373162] ------propertyName = name-------
2019-11-10 22:03:52.463156+0800 运行时[6111:373162] ------property_Value = _newName-------
2019-11-10 22:03:52.463973+0800 运行时[6111:373162] ------property_Type = @"NSString"-------

 

以上是关于分析Runtime的属性Property的主要内容,如果未能解决你的问题,请参考以下文章

runtime学习笔记

利用runtime给分类添加属性

Xcode自定义代码块

runtime之实现对应序列化

OBJC类扩展之属性字典NSObject+Property

runtime在实际开发中的应用-为类别添加属性