@前段时间面试找工作的时候,面试官问我有没有了解OC语言的动态性,objc/runtime是什么,当时的我真的是一头雾水,只是知道OC动态性,其余的还真没实际的用到过.
@回去后百度下:objective-c有两个扩展机制:category和associative。我们可以通过category来扩展方法,但是它有个很大的局限性,不能扩展属性。于是,就有了专门用来扩展属性的机制:associative。category比较常见,而associative就用的比较少。associative的主要原理,就是把两个对象相互关联起来,使得其中的一个对象作为另外一个对象的一部分。使用关联,我们可以不用修改类的定义而为其对象增加存储空间。这在我们无法访问到类的源码的时候或者是考虑到二进制兼容性的时候是非常有用。关联是基于关键字的,因此,我们可以为任何对象增加任意多的关联,每个都使用不同的关键字即可。关联是可以保证被关联的对象在关联对象的整个生命周期都是可用的(在垃圾自动回收环境下也不会导致资源不可回收).
//
// HMTSecondViewController.h
// Created by HMT on 14-8-2.
//
#import <UIKit/UIKit.h>
typedef void(^PressValueBlock)(NSString *);
@interface HMTSecondViewController : UIViewController
- (void)pressValueFromSecondToMainWithBlock:(PressValueBlock)pressValue;
@end
//
// HMTSecondViewController.m
// Created by HMT on 14-8-2.
//
#import "HMTSecondViewController.h"
#import <objc/runtime.h>
//唯一静态变量key
static char pressValue;
@interface HMTSecondViewController ()
@end
@implementation HMTSecondViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(didBackBarButtonAction:)];
self.navigationItem.title = @"测试objc";
self.view.backgroundColor = [UIColor yellowColor];
}
- (void)pressValueFromSecondToMainWithBlock:(PressValueBlock)pressValue{
/**
* @see objc_setAssociatedObject
* 设置关联
* Sets an associated value for a given object using a given key and association policy.
*
* @param object 源对象.
* @param key 是一个 void 指针,对于每个关联,key必须唯一,通常可以使用一个 static variable.
* @param value 关联的对象. Pass nil to clear an existing association.
* @param policy 关联策略,用来指定,关联的对象是 assigned, retained, copied, 还有是否是atomically.
*
* @see objc_getAssociatedObject
* @see objc_removeAssociatedObjects 可以断开所有associative。通常情况下不建议这么做,因为他会断开所有关联。
*/
objc_setAssociatedObject(self, &pressValue, pressValue, OBJC_ASSOCIATION_COPY);
}
- (void)didBackBarButtonAction:(UIBarButtonItem *)backBtn{
// 获取关联对象
PressValueBlock pressValue = objc_getAssociatedObject(self, &pressValue);
if (pressValue) {
pressValue(@"objc-runtime方式的传值");
}
[self.navigationController popViewControllerAnimated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
//
// HMTSendButton.h
// Created by HMT on 14-8-1.
//
#import <UIKit/UIKit.h>
typedef void(^ShareBlock)();
@interface HMTSendButton : UIButton
- (id)initWithClick:(ShareBlock)share;
@end
//
// HMTSendButton.m
// Created by HMT on 14-8-1.
//
#import "HMTSendButton.h"
#import <objc/runtime.h>
@implementation HMTSendButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (id)initWithClick:(ShareBlock)share{
if (self = [super init]) {
// oc运行时的关联特性 self 与 share 关联起来
objc_setAssociatedObject(self, @"share", share, OBJC_ASSOCIATION_COPY);
self.frame = CGRectMake(130, 200, 60, 40);
self.backgroundColor = [UIColor redColor];
[self addTarget:self action:@selector(didClickShareButton) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)didClickShareButton{
// 获得关联对象
ShareBlock shareBlock = objc_getAssociatedObject(self, @"share");
if (shareBlock) {
shareBlock();
}
}
@end
//
//main.m
//
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = @"objc";
self.view.backgroundColor = [UIColor cyanColor];
__weak __typeof(self) weakSelf = self;
self.shareButton = [[HMTSendButton alloc] initWithClick:^{
HMTSecondViewController *secondVC = [[HMTSecondViewController alloc] init];
[secondVC pressValueFromSecondToMainWithBlock:^(NSString *value) {
weakSelf.objcTestLabel.text = value;
}];
[weakSelf.navigationController pushViewController:secondVC animated:YES];
}];
[self.view addSubview:_shareButton];
self.objcTestLabel = [[UILabel alloc] initWithFrame:CGRectMake(60, 300, 200, 40)];
self.objcTestLabel.text = @"初始值";
[self.view addSubview:_objcTestLabel];
}