Runtime - 关联对象

马上就要开始新的项目,这个礼拜抽空来写一篇博客。于是就写一个比较贴近大家工作开发的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
#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
@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

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

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 - 关联对象)