美文网首页
Runtime - 关联对象

Runtime - 关联对象

作者: 代码堆在那_我往哪栈 | 来源:发表于2016-10-26 09:56 被阅读73次

    马上就要开始新的项目,这个礼拜抽空来写一篇博客。于是就写一个比较贴近大家工作开发的runtime其中一个用法吧。一听runtime肯定很多朋友觉得高大上,感觉我们一般工作中都没用到过。其实它在我们的工程中无处不在比如,AFnetworking,MJRefresh等等常用的第三方控件。

    关于Runtime的关联对象的使用,无非用的最多的就是两个方法

    1. objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    2. objc_getAssociatedObject(id object, const void *key)

    objc_setAssociatedObject需要四个参数:源对象,关键字,关联的对象和一个关联策略。

    关键策略 OBJC_ASSOCIATION_ASSIGN分为以下5个选择

      enum {
          OBJC_ASSOCIATION_ASSIGN = 0,                        
          对关联对象进行弱引用,通常是基本数据类型,如int,float
         
          OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 
          对关联对象进行强(strong)引用(非原子)
         
          OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   
          对关联对象进行拷贝(copy)引用(非原子)
         
          OBJC_ASSOCIATION_RETAIN = 01401,                
          对关联对象进行强(strong)引用,不是线程安全的
        
          OBJC_ASSOCIATION_COPY = 01403                   
          对关联对象进行拷贝(copy)引用,不是线程安全的
    };
    

    那么,我们什么时候用到Runtime关联对象呢?大家应该都知道类目(Gategory)吧。在类目中我们是不能像别的类一样添加属性的,但是在某种情况下,我想添加属性该怎么办?OK,这个时候runtime就起到作用了。它可以实现给类目添加属性。

    首先我们来学习一下MJRefresh,它是如何给类目添加属性的。
    以下是部分MJRefrensh源码
    UIScrollView+MJRefresh.h文件
    #import <UIKit/UIKit.h>
    #import "MJRefreshConst.h"

    @class MJRefreshHeader, MJRefreshFooter;
    
    @interface UIScrollView (MJRefresh)
    /** 下拉刷新控件 */
    @property (strong, nonatomic) MJRefreshHeader *mj_header;
    @property (strong, nonatomic) MJRefreshHeader *header MJRefreshDeprecated("使用mj_header");
    /** 上拉刷新控件 */
    @property (strong, nonatomic) MJRefreshFooter *mj_footer;
    @property (strong, nonatomic) MJRefreshFooter *footer MJRefreshDeprecated("使用mj_footer");
    
    #pragma mark - other
    - (NSInteger)mj_totalDataCount;
    @property (copy, nonatomic) void (^mj_reloadDataBlock)(NSInteger totalDataCount);
    @end
    

    UIScrollView+MJRefresh.m文件
    #pragma mark - header
    static const char MJRefreshHeaderKey = '\0';
    - (void)setMj_header:(MJRefreshHeader *)mj_header
    {
    if (mj_header != self.mj_header) {
    // 删除旧的,添加新的
    [self.mj_header removeFromSuperview];
    [self insertSubview:mj_header atIndex:0];

        // 存储新的
        [self willChangeValueForKey:@"mj_header"]; // KVO
        objc_setAssociatedObject(self, &MJRefreshHeaderKey, mj_header, OBJC_ASSOCIATION_ASSIGN);
        [self didChangeValueForKey:@"mj_header"]; // KVO
        }
      }
    
      - (MJRefreshHeader *)mj_header
      {
      return objc_getAssociatedObject(self, &MJRefreshHeaderKey);
      }
    
      #pragma mark - footer
    static const char MJRefreshFooterKey = '\0';
    - (void)setMj_footer:(MJRefreshFooter *)mj_footer
    {
        if (mj_footer != self.mj_footer) {
            // 删除旧的,添加新的
            [self.mj_footer removeFromSuperview];
            [self insertSubview:mj_footer atIndex:0];
        
            // 存储新的
            [self willChangeValueForKey:@"mj_footer"]; // KVO
            objc_setAssociatedObject(self, &MJRefreshFooterKey,mj_footer, OBJC_ASSOCIATION_ASSIGN);
            [self didChangeValueForKey:@"mj_footer"]; // KVO
        }
    }
    
    - (MJRefreshFooter *)mj_footer
    {
        return objc_getAssociatedObject(self, &MJRefreshFooterKey);
    }
    

    很明显,这里是使用了set和get方法,但是在方法里面写的内容又跟我们以前写的set和get方法不一样了。runtime关联简单理解就是将一个值存入一个键值对,通过对应的key取得对应的value
    object:与谁关联,通常是传self
    key:唯一键,在获取值时通过该键获取,通常是使用static const void *来声明
    value:关联所设置的值
    policy:内存管理策略,比如使用copy

    大家看了上面这个实例应该对Runtime的关联有一定的了解,那么我再加一个我个人写的demo,我相信看了这个,大家应该会有一个更深的理解。demo在GitHub下载

    ViewController.m文件中
    #import "ViewController.h"
    #import "UIControl+tyBlock.h"
    #import <objc/runtime.h>
    @interface ViewController ()

    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad 
    {
      [super viewDidLoad];
      NSArray *array1 = @[@"1",@"2",@"3"];
      objc_setAssociatedObject(self, @"dictionary123", array1,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
      UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
      [btn setTitle:@"点我" forState:UIControlStateNormal];
      [self.view addSubview:btn];
      [btn setFrame:CGRectMake(50, 50, 50, 50)];
      btn.backgroundColor = [UIColor redColor];
      [btn addTarget:self action:@selector(AlertContro:) forControlEvents:UIControlEventTouchUpInside];
    
      UIButton *abcBtn = [UIButton buttonWithType:UIButtonTypeCustom];
      [abcBtn setTitle:@"别摸我" forState:UIControlStateNormal];
      [self.view addSubview:abcBtn];
      [abcBtn setFrame:CGRectMake(250, 50, 50, 50)];
      abcBtn.backgroundColor = [UIColor greenColor];
      abcBtn.zty_block = ^(id sender){
          NSLog(@"%@",sender);
      };
      UIButton *bb = [[UIButton alloc] init];
      bb.name = @"runtime属性添加";
      NSLog(@"%@",bb.name);
    }
    
    - (void)AlertContro:(UIButton *)button
    {
      UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"你个流氓" message:@"嘿嘿"   preferredStyle:UIAlertControllerStyleAlert];
    
      [alert addAction:[UIAlertAction actionWithTitle:@"调戏你" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
        NSArray *array1 = objc_getAssociatedObject(self, @"dictionary123");
        NSLog(@"%@",array1[0]);
    }]];
    
      [alert addAction:[UIAlertAction actionWithTitle:@"放了你" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"放了你");
    }]];
    
    //取消
      UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
      }];
      [alert addAction:cancel];
      [self presentViewController:alert animated:YES completion:nil];
    }
    

    UIControl+tyBlock.h文件
    #import <UIKit/UIKit.h>

    typedef void (^ZTYTouchBlock)(id sender);
    
    @interface UIControl (tyBlock)
    
    @property (nonatomic, copy) ZTYTouchBlock zty_block;
    @property (nonatomic, strong) NSString *name;
    @end
    

    UIControl+tyBlock.m文件
    #import "UIControl+tyBlock.h"
    #import <objc/runtime.h>

    static const void *ztyUIControlTouchUpEventBlockKey = "ztyUIControlTouchUpEventBlockKey";
    
    @implementation UIControl (tyBlock)
    
    - (void)setZty_block:(ZTYTouchBlock)zty_block{
    
        objc_setAssociatedObject(self, ztyUIControlTouchUpEventBlockKey, zty_block,         OBJC_ASSOCIATION_COPY_NONATOMIC);
        [self removeTarget:self
                action:@selector(ztyAction:)
            forControlEvents:UIControlEventTouchUpInside];
    
        if (zty_block) {
            [self addTarget:self
                     action:@selector(ztyAction:)
           forControlEvents:UIControlEventTouchUpInside];
        }
    }
    
    - (ZTYTouchBlock)zty_block{
    
        return objc_getAssociatedObject(self, ztyUIControlTouchUpEventBlockKey);
    }
    
    - (void)ztyAction:(id)sender{
        ZTYTouchBlock touchBlock = self.zty_block;
        UIButton *button = sender;
        if (touchBlock) {
            touchBlock(button.titleLabel.text);
        }
    }
    
    - (void)setName:(NSString *)name{
        objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    - (NSString *)name{
        return objc_getAssociatedObject(self, "name");
    }

    相关文章

      网友评论

          本文标题:Runtime - 关联对象

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