美文网首页
有关runtime的一点记录

有关runtime的一点记录

作者: 请叫我魔法师 | 来源:发表于2017-10-24 16:15 被阅读0次

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

    看了看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 <objc/runtime.h>
    //关联对象的静态变量的关键字
    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 <UIKit/UIKit.h>
    
    @interface ShareView : UIView
    
    - (ShareView *)initWithFame:(CGRect)frame Result:(void (^)(BOOL result))block;
    
    @end
    

    m

    #import "ShareView.h"
    #import <objc/runtime.h>
    
    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的一点记录

          本文链接:https://www.haomeiwen.com/subject/ltxxpxtx.html