马上就要开始新的项目,这个礼拜抽空来写一篇博客。于是就写一个比较贴近大家工作开发的runtime其中一个用法吧。一听runtime肯定很多朋友觉得高大上,感觉我们一般工作中都没用到过。其实它在我们的工程中无处不在比如,AFnetworking,MJRefresh等等常用的第三方控件。
关于Runtime的关联对象的使用,无非用的最多的就是两个方法
- objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- 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");
}
网友评论