一 关联的3个方法
- objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
/**
* 关联你需要关联的对象
*
* @param object 调用我的类,常见的是self
* @param key 一个关联的key值。有点类似,字典的key和广播的Name
* @param value 关联的对象 特别:设置成nil,则表示删除该对象的某一个扩展属性
* @param policy 关联的方式 OBJC_ASSOCIATION_RETAIN_NONATOMIC 等
*/
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- objc_getAssociatedObject(id object, void *key)
/**
* 获取关联的对象
*
* @param object 调用我的类,常见的是self
* @param key 一个关联的key值。有点类似,字典的key和广播的Name
*/
objc_getAssociatedObject(id object, void *key)
- objc_removeAssociatedObjects(id object)
/**
* 打断对象的所有关联
*
* @param object 要删除关联的对象,常见的是self
*/
objc_removeAssociatedObjects(id object)
tips: 使用objc_setAssociatedObject等,需要引入头文件 #import objc/runtime.h
二 关联的主要作用是扩展属性。
主要作用是当属性和全局变量不能使用的时候进行扩展属性,说白了就是在我配合category会更好的效果,为什么?因为使用时需要key值,一般是一个不可变的字符串,如果在一类中大量的调用我会产生很多不同的key值,这样不便于维护
类别是不可以添加属性的。平常看到在 category 中 写了 @property 实际是生成2个方法罢了
三 应用:
3.1. 用UIAlertview来示例关联与方法交换
如果要给UIAlertview加上一个block 的点击事件,一般的做法是重写UIAlertview,在子类中加上点击的block。这样耦合度太高了,如果我们能够写一个category实现alertview的点击block 就好了,但是category是不允许属性的,所以我们得用另外一种方式实“现曲线救国”。
//UIAlertView+Associate.h
#import <UIKit/UIKit.h>
typedef void (^bSuccessBlock)(NSInteger buttonIndex);
@interface UIAlertView (Associate)<UIAlertViewDelegate>
-(void)showWithBlock:(bSuccessBlock)block;
@end
//UIAlertView+Associate.m
#import "UIAlertView+Associate.h"
#import <objc/runtime.h>
static const char alertKey;
@implementation UIAlertView (Associate)
-(void)showWithBlock:(bSuccessBlock)block{
if (block) {
objc_setAssociatedObject(self, &alertKey, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.delegate = self;
}
[self show];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
bSuccessBlock block = objc_getAssociatedObject(self, &alertKey);
block(buttonIndex);
}
@end
使用
UIAlertView *tAlertView = [[UIAlertView alloc]initWithTitle:@"me" message:@"" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"其他", nil];
[tAlertView showWithBlock:^(NSInteger buttonIndex) {
NSLog(@"%ld",(long)buttonIndex);
}];
拓展
1 @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的?
“属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。Objective-C 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。
1.1 @property 的本质是什么?
@property =ivar(实例变量)+access method (存取方法= getter + setter);
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
//相当于下边,生成生成存取方法+实例变量
@interface Person : NSObject{
NSString *_firstName;
NSString *_lastName;
}
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end
1.2 ivar、getter、setter 是如何生成并添加到这个类中的?
“自动合成”( autosynthesis):完成属性定义后,编译器会自动编写访问这些属性所需的方法。
这个过程由编译器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。
//属性定义后会自动生成两个实例变量,其名称分别为 _firstName 与 _lastName
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
//或者
//也可以通过 @synthesize 语法来指定实例变量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
1.3 属性是怎么实现的
- OBJC_IVAR_类名$属性名称 :该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
- setter 与 getter 方法对应的实现函数
- ivar_list :成员变量列表
- method_list :方法列表
- prop_list :属性列表
也就是说我们每次在增加一个属性:
(1)系统都会在 ivar_list 中添加一个成员变量的描述,
(2)在 method_list 中增加 setter 与 getter 方法的描述,
(3)在prop_list 中增加一个属性的描述,
(4)然后计算该属性在对象中的偏移量,
(5)然后给出 setter 与 getter 方法对应的实现,
(6)在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.
1.4 @protocol 和 category 中如何使用 @property
- 在 protocol 中使用 property 只会生成 setter 和 getter 方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性
- category 使用 @property 也是只会生成 setter 和 getter 方法的声明,如果我们真的需要给 category 增加属性的实现,需要借助于运行时的两个函数:
- objc_setAssociatedObject
- objc_getAssociatedObject
1.[Objective-c 高阶语法] 关联与方法交换
2.ios设计模式
3.@property 的本质是什么?
4.iOS Category中添加属性和成员变量的区别
5.Objective-C类成员变量深度剖析
6.让我们来谈谈 @property