有关runtime的一点记录

虽然很早很早就知道有这么个牛逼的存在。看过一些皮毛,看完之后哦了几声感觉高大上,仿佛看懂了。不过过了一会就不知道刚才看的是什么了!

看了看MJRefresh和YYModel源码中,看到了这个方法,使用频率挺高。记录一下,多一些认识

1、 关联对象方法

objc_setAssociatedObject / objc_getAssociatedObject
一看名字就知道,类似get和set方法。一个赋值一个取值。

来把一个对象与另外一个对象进行关联。该函数需要四个参数:源对象,关键字,关联的对象和一个关联策略。

OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

1.OBJC_EXPORT 打包lib时,用来说明该函数是暴露给外界调用的。
2.id object 表示关联者,是一个对象,变量名理所当然也是object
3.id value 表示被关联者,变量名是value,它要关联到object上的。
4.关键字是一个void类型的指针。每一个关联的关键字必须是唯一的。通常都是会采用静态变量来作为关键字。
5.关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的;还有这种关联是原子的还是非原子的。这里的关联策略和声明属性时的很类似。这种关联策略是通过使用预先定义好的常量来表示的。
关联策略我感觉我和声明属性一样,retain和copy也是对应的。

enum { OBJC_ASSOCIATION_ASSIGN = 0,
       OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
       OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
       OBJC_ASSOCIATION_RETAIN = 01401,
       OBJC_ASSOCIATION_COPY = 01403};

6.断开关联
断开关联是使用objc_setAssociatedObject函数,传入nil值即可。
使用函数objc_removeAssociatedObjects可以断开所有关联。

一个简单例子,给一个数组添加一个属性。

#import "ViewController.h"
#import 
//关联对象的静态变量的关键字
static const NSString *associationKey = @"associationKey";
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    NSArray *arr = @[@"11", @"22", @"33"];
    NSString *str = @"44";
    
//添加一个属性对象
    objc_setAssociatedObject(arr, &associationKey, str, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    NSString *getStr = objc_getAssociatedObject(arr, &associationKey);

    NSLog(@"666_SunDePrint_999:%@\n%@", getStr, arr);
}
@end

再看一个例子
需求:自定义了分享面板,点击分享QQ或者微信后,我需要再当前页面获得分享的结果,成功或者失败。
由于是自定义view,QQ微信的图标按钮就需要自己写个button,分享结果来自微信分享SDK的回调。所以初始化自定义分享面板方法的时候,需要写个block作为参数,传递成功和失败的结果。(描述的不清楚)
由于每个按钮是没关系的,所以点击各个分享按钮后,得到各自对应的结果,需要回调到各自按钮的点击事件里。
所以就需要每个按钮有个block属性,才能在点击事件里调用控制器中的block做想做的事。

1、自定义的view,作为分享view
上面一个按钮,点击后就调用分享SDK,获得结果传递给控制器。
h

#import 

@interface ShareView : UIView

- (ShareView *)initWithFame:(CGRect)frame Result:(void (^)(BOOL result))block;

@end

m

#import "ShareView.h"
#import 

static NSString *share_weixin_key = @"share_weixin_key";

@implementation ShareView

- (ShareView *)initWithFame:(CGRect)frame Result:(void (^)(BOOL result))block {
    
    self = [super initWithFrame:frame];
    
    if (self) {
        
        UIButton *weixinBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, frame.size.width/2, frame.size.height/2)];
        [weixinBtn setBackgroundImage:[UIImage imageNamed:@"111"] forState:0];
        [weixinBtn addTarget:self action:@selector(weixinClick:) forControlEvents:64];
        //添加一个属性对象
        objc_setAssociatedObject(weixinBtn, &share_weixin_key, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
        
        [self addSubview:weixinBtn];
    }
    
    return self;
}

- (void)weixinClick:(UIButton *)sender {
    
//根据key,取出添加的属性
    void (^block)(BOOL result) = objc_getAssociatedObject(sender, &share_weixin_key);
    
    sleep(2);//模拟分享消耗2s
    //2s后,得到结果成功或者失败
    if (block) {
        block(YES);
    }
}

2、在控制器中的调用

    ShareView *share = [[ShareView alloc] initWithFame:CGRectMake(0, 100, self.view.frame.size.width, 200) Result:^(BOOL result){
        
        if (result == YES) {
            NSLog(@"666_SunDePrint_999:%@", @"分享成功");
        }else{
            NSLog(@"666_SunDePrint_999:%@", @"分享成功");
        }
    }];
    
    share.backgroundColor = [UIColor greenColor];
    [self.view addSubview:share];

这算是runtime关联对象方法的一个应用了。当然可以用别的方法传递结果啥的,我自定义的分享面板view好像有些问题。哈哈。

3、给category添加属性
一般用category都是用来添加方法用的,不能添加属性,但是有时候确实需要额外一些属性,就可以用关联方法来添加属性了。MJRefresh等三方库中经常用到。

2、获取对象所有属性方法

class_copyPropertyList 、class_copyIvarList
有2个方法用法一样,前者是获取属性方法,后者还可以额外获取成员变量。最后记得释放数组。
参数传入当前类和一个数值,获取属性后,可以给这个数值赋值,得到属性的数量。返回一个属性数组。

class_copyPropertyList 相关方法

// 获取所有属性
class_copyPropertyList
说明:使用class_copyPropertyList并不会获取无@property声明的成员变量
// 获取属性名
property_getName
// 获取属性特性描述字符串
property_getAttributes
// 获取所有属性特性
property_copyAttributeList

class_copyIvarList相关方法

// 获取所有成员变量
class_copyIvarList
// 获取成员变量名
ivar_getName
// 获取成员变量类型编码
ivar_getTypeEncoding
// 获取指定名称的成员变量
class_getInstanceVariable
// 获取某个对象成员变量的值
object_getIvar
// 设置某个对象成员变量的值
object_setIvar

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    Class selfCla = [self class];
    
    unsigned int proCunt;
    objc_property_t *pros = class_copyPropertyList(selfCla, &proCunt);
    NSLog(@"666_SunDePrint_999:%d", proCunt);
    for (int i = 0; i < proCunt; i++) {
        objc_property_t pro = pros[i];
        NSString *proName = [[NSString alloc] initWithCString:property_getName(pro) encoding:NSUTF8StringEncoding];
        NSLog(@"666_SunDePrint_999:%@", proName);
    }
    free(pros);
    
    unsigned int varCount;
    Ivar *porList = class_copyIvarList(selfCla, &varCount);
    NSLog(@"666_SunDePrint_999:%d", varCount);
    for (int i = 0; i < varCount; i++) {
        Ivar list = porList[i];
        NSString *name = [[NSString alloc] initWithCString:ivar_getName(list) encoding:NSUTF8StringEncoding];
        NSLog(@"666_SunDePrint_999--var:%@", name);
    }
    free(porList);
}

3、获取有关属性信息的结构体

property_getAttributes函数返回objc_property_attribute_t结构体列表,objc_property_attribute_t结构体包含name和value,常用的属性如下:

属性类型 name值:T value:变化
编码类型 name值:C(copy) &(strong) W(weak)空(assign) 等 value:无
非/原子性 name值:空(atomic) N(Nonatomic) value:无
变量名称 name值:V value:变化

使用property_getAttributes获得的描述是property_copyAttributeList能获取到的所有的name和value的总体描述,如 T@"NSDictionary",C,N,V_dict1

  for (int i = 0; i < proCunt; i++) {
        objc_property_t pro = pros[i];
        NSString *proName = [[NSString alloc] initWithCString:property_getName(pro) encoding:NSUTF8StringEncoding];
        
        
        const char *attrs = property_getAttributes(pro);
        NSString *propertyAttributes = @(attrs);
        NSArray *attributeItems = [propertyAttributes componentsSeparatedByString:@","];

        NSLog(@"666_SunDePrint_999:%@--%@", proName, attributeItems);
    }

你可能感兴趣的:(有关runtime的一点记录)