Objective-C有两个扩展机制:Associative和Category。Category用来扩展类方法,Associative用于扩展属性。Associative机制的原理是把两个对象关联起来,让一个对象成为另外一个对象的一部分。它可以在不修改类的定义的前提下为其对象增加存储空间,这在我们无法访问类的源码时(例如给UILable添加一个selected的BOOL属性)是非常有用的。Associative基于关键字的,因此我们可以使用不同的关键字为任何对象添加任意多的Associative。Associative可以保证被关联的对象在对象的整个生命周期都是可用的。Associative基于runtime,是运行时里的东西,所以头文件需要引用#im port<objc/runtime.h>文件。
Associative提供了三个方法:
1 objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) 2 objc_getAssociatedObject(id object, const void *key) 3 objc_removeAssociatedObjects(id object)
详细介绍一下这几个方法,第一个用于设置关联的:
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
注意的是返回值类型为Object类型,注意一些不是Object类型的例如:BOOL是结构类型。当碰到这种情况可以考虑通过中间类型来转换,如设置BOOL类型属性的时候可以转换为NSNumber类型,获取的时候再转换成BOOL类型即可。
四个参数分别是:源对象,关键字,关联对象和关联策略
关键字是一个void类型的指针,例如 static void * myKey = (void *)@"MyKey";每一个关联的关键字必须是唯一的。关联策略是枚举类型:
1 enum{ 2 OBJC_ASSOCIATION_ASSIGN;----------->@property(assign)----------->弱引用关联对象 3 OBJC_ASSOCIATION_COPY;------------->@property(copy,atomic)------>复制关联对象且为原子操作 4 OBJC_ASSOCIATION_COPY_NONATOMIC;--->@property(copy,nonatomic)--->复制关联对象且为非原子操作 5 OBJC_ASSOCIATION_RETAIN;----------->@property(strong,atomic)---->强引用关联对象且为原子操作 6 OBJC_ASSOCIATION_RETAIN_NONATOMIC;->@property(strong,nonatomic)->强引用且为非原子操作 7 }
第二个用于获取关联对象:
objc_getAssociatedObject(id object, const void *key) 用于获取关联对象的值。这里需要注意的是返回值类型为Object类型,注意一些不是Object类型的例如:BOOL是结构类型
第三个 objc_removeAssociatedObjects(id object) 是断开关联,需要注意的是他会断开所有关联,所以不推荐这种方式。需要断开关联的时候使用objc_setAssociatedObject函数,传入nil值即可。
下面一个示例,我给UILable添加了一个表示选中状态的selected属性,自定义了类似于UIButton的选中非选中状态下设置背景色,字体色的方法。
.h文件
1 #import <UIKit/UIKit.h> 2 #import <objc/runtime.h> 3 typedef enum{ 4 ControlStateNormal, 5 ControlStateSelected, 6 }ControlState; 7 8 @interface UILabel (CellLable) 9 10 11 @property(nonatomic,assign)BOOL selected;//设置UILable的选中和非选中状态 default is NO 12 13 -(void)setBackgroundColor:(UIColor *)backgroundColor forState:(ControlState)state ; 14 15 -(void)setTextColor:(UIColor *)textColor forState:(ControlState)state ; 16 17 18 @end
.m文件
1 #import "UILabel+CellLable.h" 2 3 static UIColor * normalBackgroundColor = nil ; 4 static UIColor * selectedBackgorundColor = nil ; 5 static UIColor * normalTextColor = nil ; 6 static UIColor * selectedTextColor = nil ; 7 static UIColor * firstBackgroundColor = nil ; 8 static UIColor * firstTextColor = nil ; 9 10 static BOOL isHaveSet = NO; //用于在未调用setSelected方法时 设置UILable的默认属性 11 12 //static char const myKey = 'a'; 13 static void * myKey = (void *)@"MyKey"; 14 15 @implementation UILabel (CellLable) 16 17 @dynamic selected ;//动态绑定 声明自定义set get 方法,不自动生成 18 19 -(void)setSelected:(BOOL)selected{ 20 21 #warning 注意 runtime 里边的objc_setAssociateObject 方法 第三个参数为object类型 BOOL属于结构类型 需要用NSNumber来转换 22 isHaveSet = YES; 23 24 if (selected) { 25 26 [self setBackgroundColor:nil forState:ControlStateSelected]; 27 28 [self setTextColor:nil forState:ControlStateSelected]; 29 30 }else if (!selected){ 31 32 [self setBackgroundColor:nil forState:ControlStateNormal]; 33 34 [self setTextColor:nil forState:ControlStateNormal]; 35 } 36 37 NSNumber * num = [NSNumber numberWithBool:selected]; 38 39 objc_setAssociatedObject(self, &myKey, num, OBJC_ASSOCIATION_ASSIGN); 40 } 41 42 -(BOOL)selected{ 43 44 NSNumber * num = objc_getAssociatedObject(self, &myKey); 45 46 return [num boolValue]; 47 } 48 -(void)setBackgroundColor:(UIColor *)backgroundColor forState:(ControlState )state{ 49 50 switch (state) { 51 case ControlStateNormal: 52 { 53 /** 当传递进来的是nil的时候,采取一种保护机制 **/ 54 if (backgroundColor) { 55 56 /** 记录上次传递进来的颜色属性 **/ 57 normalBackgroundColor = backgroundColor ; 58 } 59 self.backgroundColor = normalBackgroundColor ; 60 } 61 break; 62 case ControlStateSelected: 63 { 64 if (backgroundColor) { 65 selectedBackgorundColor = backgroundColor ; 66 } 67 self.backgroundColor = selectedBackgorundColor ; 68 } 69 break ; 70 default: 71 break; 72 } 73 74 if (!isHaveSet) { 75 76 self.backgroundColor = normalBackgroundColor ; 77 } 78 } 79 80 -(void)setTextColor:(UIColor *)textColor forState:(ControlState)state{ 81 82 switch (state) { 83 case ControlStateNormal: 84 { 85 if (textColor) { 86 normalTextColor = textColor ; 87 } 88 self.textColor = normalTextColor ; 89 } 90 break; 91 case ControlStateSelected: 92 { 93 if (textColor) { 94 selectedTextColor = textColor ; 95 } 96 self.textColor = selectedTextColor ; 97 } 98 break ; 99 default: 100 break; 101 } 102 103 if (!isHaveSet) { 104 self.textColor = normalTextColor ; 105 } 106 } 107 108 @end
ViewController.m文件
1 -(void)viewDidLoad{ 2 UILabel * lable2 =[[UILabel alloc]initWithFrame:CGRectMake(20, 13, 20, 20)]; 3 4 lable2.textAlignment = NSTextAlignmentCenter ; 5 6 lable2.layer.masksToBounds = YES ; 7 8 lable2.layer.cornerRadius =10 ; 9 10 lable2.selected = NO ; 11 12 [lable2 setBackgroundColor:[UIColor colorWithRed:48/255.0 green:190/255.0 blue:108/255.0 alpha:1.0] forState:ControlStateSelected]; 13 [lable2 setBackgroundColor:[UIColor clearColor] forState:ControlStateNormal]; 14 15 16 [lable2 setTextColor:[UIColor whiteColor] forState:ControlStateSelected]; 17 [lable2 setTextColor:[UIColor blackColor] forState:ControlStateNormal]; 18 19 20 [self.view addSubview:lable2]; 21 }