iOS程序猿IOSNSObject的前世今生

iOS runtime(一)runtime之Property 详

2018-05-24  本文已影响131人  奔跑吧小蚂蚁

runtime官方文章学习大纲

1. 读取类的Property属性

1.1相关函数

typedef struct objc_property *objc_property_t;

  typedef struct {
      const char *name;           
      const char *value;          
  } objc_property_attribute_t;


// 获取指定的属性
objc_property_t class_getProperty ( Class cls, const char *name );
// 获取属性列表
objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount );
//获取属性名称
const char * property_getName(objc_property_t _Nonnull property) 
//获取属性特性
const char *property_getAttributes(objc_property_t _Nonnull property) 
//获取属性特性列表
objc_property_attribute_t *property_copyAttributeList(objc_property_t property,unsigned int * outCount)
//获取根据特定键获取想得到的属性特性
char *property_copyAttributeValue(objc_property_t  property, const char * attributeName)

1.2.案例代码:

#import "ViewController.h"
#import <objc/runtime.h>

@interface ViewController ()

@property (nonatomic,strong)NSString *name;
@property (atomic,strong)NSString *age;
@property (nonatomic,assign)BOOL isMan;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self class_copyPropertyList];
    
}

- (void)class_copyPropertyList{
    //得到当前class的所有属性
    unsigned int count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    for (int i = 0; i<count; i++) {
        objc_property_t property = properties[i];
        //属性名
        //const char * property_name = property_getName(property);
        //属性描述
        //const char * property_attr = property_getAttributes(property);
        //获取属性类型
        //const char *propertyType = property_copyAttributeValue(property, "T");
        //属性名
        //const char *property_Value = property_copyAttributeValue(property, "V");
        //NSLog(@"property_name:%s \n property_attr:%s \n propertyType:%s \n property_Value:%s",property_name, property_attr, propertyType,property_Value);
        //属性名
        NSString *property_name = @(property_getName(property));
        //属性描述
        NSString *property_attr = @(property_getAttributes(property));
        //获取属性类型
        NSString *propertyType = @(property_copyAttributeValue(property, "T"));
        //属性名
        NSString *property_Value = @(property_copyAttributeValue(property, "V"));
        NSLog(@"\n property_name:%@ \n property_attr:%@ \n propertyType:%@ \n property_Value:%@",property_name,property_attr,propertyType,property_Value);
        unsigned int attributeCount;
        objc_property_attribute_t *attributeList = property_copyAttributeList(property, &attributeCount);
        for (unsigned int j = 0; j < attributeCount; j++) {
            objc_property_attribute_t attribute = attributeList[j];
            //const char *name = attribute.name;
            //const char *value = attribute.value;
            //NSLog(@"attribute name: %s, value: %s", name, value);
            NSString *name = @(attribute.name);
            NSString *value = @(attribute.value);
            NSLog(@"attribute name: %@, value: %@", name, value);
        }
        free(attributeList);
    }
    free(properties);
}

1.3.打印数据:

2018-05-24 14:48:35.663763+0700 runtime [28461:2078183] 
 property_name:name 
 property_attr:T@"NSString",&,N,V_name 
 propertyType:@"NSString" 
 property_Value:_name
2018-05-24 14:48:35.663940+0700 runtime [28461:2078183] attribute name: T, value: @"NSString"
2018-05-24 14:48:35.664034+0700 runtime [28461:2078183] attribute name: &, value:
2018-05-24 14:48:35.664124+0700 runtime [28461:2078183] attribute name: N, value:
2018-05-24 14:48:35.664259+0700 runtime [28461:2078183] attribute name: V, value: _name
2018-05-24 14:48:35.664399+0700 runtime [28461:2078183] 
 property_name:age 
 property_attr:T@"NSString",&,V_age 
 propertyType:@"NSString" 
 property_Value:_age
2018-05-24 14:48:35.664516+0700 runtime [28461:2078183] attribute name: T, value: @"NSString"
2018-05-24 14:48:35.664600+0700 runtime [28461:2078183] attribute name: &, value:
2018-05-24 14:48:35.664677+0700 runtime [28461:2078183] attribute name: V, value: _age
2018-05-24 14:48:35.664756+0700 runtime [28461:2078183] 
 property_name:isMan 
 property_attr:TB,N,V_isMan 
 propertyType:B 
 property_Value:_isMan
2018-05-24 14:48:35.664850+0700 runtime [28461:2078183] attribute name: T, value: B
2018-05-24 14:48:35.664934+0700 runtime [28461:2078183] attribute name: N, value:
2018-05-24 14:48:35.665079+0700 runtime [28461:2078183] attribute name: V, value: _isMan

1.4.分析:

1.5.特性编码解析

1.property_attribute为T@”NSString”,&,N,V_exprice时:
T 是固定的,放在第一个
@”NSString” 代表这个property是一个字符串对象
& 代表强引用,其中与之并列的是:’C’代表Copy,’&’代表强引用,’W’表示weak,assign为空,默认为assign。
N 区分的nonatomic和atomic,默认为atomic,atomic为空,’N’代表是nonatomic
V_exprice V代表变量,后面紧跟着的是成员变量名,代表这个property的成员变量名为_exprice。

2.property_attribute为T@”NSNumber”,R,N,V_yearsOld时:
T 是固定的,放在第一个
@”NSNumber” 代表这个property是一个NSNumber对象
R 代表readOnly属性,readwrite时为空
N 区分的nonatomic和atomic,默认为atomic,atomic为空,’N’代表是nonatomic
V_yearsOld V代表变量,后面紧跟着的是成员变量名,代表这个property的成员变量名为_yearsOld。

3.对应的编码值
//下面对应的编码值可以在官方文档里面找到
//编码值   含意
//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…}  代表结构体
//(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)

4.其他
G(name) getter=(name)
S(name) setter=(name)
D @dynamic
P 用于垃圾回收机制

推荐解析文章
特性编码解析大全
案例图

案例图.png

1.6查漏补缺

获取单个属性的信息

#import "ViewController.h"
#import <objc/runtime.h>

@interface ViewController ()
@property (nonatomic,strong)NSString *name;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self class_getProperty:[self class] name:"name"];
}

- (void)class_getProperty:(Class)class name:(const char *)name {
    objc_property_t property = class_getProperty(class,name);
    NSString *property_name = @(property_getName(property));
    NSString *property_attr = @(property_getAttributes(property));
    NSString *property_Value = @(property_copyAttributeValue(property, "V"));
    NSString *property_Type = @(property_copyAttributeValue(property, "T"));
    NSLog(@"\n property_name:%@ \n property_attr:%@,\n property_Type:%@ \n property_Value:%@",property_name,property_attr,property_Type,property_Value);
}
打印数据:
2018-05-24 16:51:58.266463+0700 runtime [29718:2161350] 
 property_name:name 
 property_attr:T@"NSString",&,N,V_name,
 property_Type:@"NSString" 
 property_Value:_name

看代码就明白了,不解释了,很多知识由面到点,会更精彩一些。

2.替换类的Property属性

相关函数

// 替换类的属性
void class_replaceProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );

实例代码:

#import "ViewController.h"
#import <objc/runtime.h>


@interface ViewController ()

@property (nonatomic,strong)NSString *name;
@property (nonatomic,assign)NSString *age;
@property (nonatomic,strong)NSString *address;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self class_replaceProperty:[self class] class_name:"name" replace_name:"nick_name"];
    //对name进行特性获取
    [self class_getProperty:[self class] name:"name"];
}

- (void)class_replaceProperty:(Class)class class_name:(const char *)class_name replace_name:(const char *)replace_name{
    objc_property_attribute_t type = { "T", "@\"NSString\"" };
    objc_property_attribute_t ownership = { "C", "" }; // C = copy
    objc_property_attribute_t backingivar  = { "V", replace_name };
    objc_property_attribute_t attrs[] = { type, ownership, backingivar };
    class_replaceProperty(class, class_name, attrs, 3);
}
- (void)class_getProperty:(Class)class name:(const char *)name {
    objc_property_t property = class_getProperty(class,name);
    NSString *property_name = @(property_getName(property));
    NSString *property_attr = @(property_getAttributes(property));
    NSString *property_Value = @(property_copyAttributeValue(property, "V"));
    NSString *property_Type = @(property_copyAttributeValue(property, "T"));
    NSLog(@"\n property_name:%@ \n property_attr:%@,\n property_Type:%@ \n property_Value:%@",property_name,property_attr,property_Type,property_Value);
}

分析:
从打印数据的数据我们对比可以发,所谓替换属性并不是说直接讲声明的名称改掉,而是将属性特性值改为我们所需要的。

3.为类添加Property

相关函数

 BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)

实例代码

#import "ViewController.h"
#import <objc/runtime.h>


@interface ViewController ()

@property (nonatomic,strong)NSString *name;
@property (nonatomic,assign)NSString *age;
@property (nonatomic,strong)NSString *address;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //新增nick_name
    [self class_addProperty:[self class] add_name:"nick_name"];
    //对nick_name进行特性获取
    [self class_getProperty:[self class] name:"nick_name"];
}



- (void)class_addProperty:(Class)class add_name:(const char *)add_name {
    objc_property_attribute_t type = { "T", "@\"NSString\"" };
    objc_property_attribute_t ownership = { "&", "N" }; // C = copy
    objc_property_attribute_t backingivar  = { "V", "" };
    objc_property_attribute_t attrs[] = { type, ownership, backingivar };
    if (class_addProperty(class, add_name, attrs, 3)) {
        NSLog(@"添加属性--%sadd Property success",__func__);
    }else{
        NSLog(@"添加属性--%sadd Property fail",__func__);
    }
}
- (void)class_getProperty:(Class)class name:(const char *)name {
    objc_property_t property = class_getProperty(class,name);
    NSString *property_name = @(property_getName(property));
    NSString *property_attr = @(property_getAttributes(property));
    NSString *property_Value = @(property_copyAttributeValue(property, "V"));
    NSString *property_Type = @(property_copyAttributeValue(property, "T"));
    NSLog(@"\n property_name:%@ \n property_attr:%@,\n property_Type:%@ \n property_Value:%@",property_name,property_attr,property_Type,property_Value);
   
}

打印数据:

2018-05-26 09:29:05.939306+0700 runTimer[5930:999110] 添加属性---[ViewController class_addProperty:add_name:]add Property success
2018-05-26 09:29:05.939474+0700 runTimer[5930:999110] 
 property_name:nick_name 
 property_attr:T@"NSString",&N,V,
 property_Type:@"NSString" 
 property_Value:

分析:
对比替换属性进行分析,会更加透彻。

runtime之Property 讲解就完毕了。不足指出还请指正。

下一章:Ivar 详解晚更~

上一篇下一篇

猜你喜欢

热点阅读