有这么一个需求,为系统的类添加一个方法,可能大家都会想到category;可如果,要为类添加一个实例呢?我们都知道,category不能添加实例对象,那应该怎么办呢? 还好,Objective-C有一项强大的特性可以解决此问题,这就是“关联对象”(Associated Object)
主要通过三个方法来管理:
1.void objc_setAssociatedObject (id object, void*key, id value, objc_AssociationPolicy policy)
此方法以给定的键和策略为对象设置关联对象值
2.id objc_getAssociatedObject (id object, void*key)
此方法根据给定的键从某对象中获取相应的关联对象值
3.void objc_removeAssociatedObjects(id object)
此方法移除指定对象的全部关联对象
观察上面三种方法,是不是发现和NSDictionary存、取、删除数据很像,我们可以这样加深理解。不过注意,两者有个重要差别,设置关联对象用的键是个“不透明指针”,就是说其指向的数据结构不局限于某种特定类型的指针。
下面上代码:
下面上代码:
//
// UIView+FlyView.h
// runtime_property
//
// Created by fly on 16/4/25.
// Copyright © 2016年 HelloBaby. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIView (FlyView)
@property (nonatomic, copy) NSString *tagStr;
@end
#import "ViewController.h"
#import "UIView+FlyView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView *flyView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 50, 50)];
flyView.backgroundColor = [UIColor yellowColor];
flyView.tagStr = @"1";
[self.view addSubview:flyView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
我们知道,UIView有个Tag属性,支持int类型,可是我想让他拥有一个支持NString类型,所以我在category给他增加tagStr属性。这样在创建时没报错,但是在运行时会发现,崩溃,错误原因`’-[UIView setTagStr:]: unrecognized selector sent to instance 0x7f856347baf0’
category不支持实例变量,虽然我们创建的是属性,可底层还是会调用setter方法设置实例的。这种情况下,我们就可以通过上面轻松实现
#import "UIView+FlyView.h"
#import <objc/runtime.h>
@implementation UIView (FlyView)
- (void)setTagStr:(NSString *)tagStr
{
/* 参数
* 第一个参数:将关联值设置到的实例
* 第二个参数:关联值的标识key,不只是NSString,它支持任何类型
* 第三个参数:关联值自身
* 第四个参数:关联属性的存储策略,枚举,对应我们申明属性时属性的属性·
*/
objc_setAssociatedObject(self, @selector(tagStr), tagStr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)tagStr
{
return objc_getAssociatedObject(self, @selector(tagStr));
}
@end
其他的不需要变,我们只需要在.m里设置下关联值,就可以为系统类动态增加属性。运行一下,很轻松的就运行起来了。