全文引自
《Effective Objective-C 2.0 编写高质量iOS与OS X代码发的52个有效方法》,旨在为大家提供一点思路。Github 笔记
涉及到的方法
objc_setAssociatedObject
objc_getAssociatedObject
有时需要在对象中存储相关信息,这时我们通常会从对象所属的类中继承一个子类,然后改用这个子类对象。然而,并非所有情况都能这么做,有时候类的实例可能是由某种机制所创建的,而开发者无法令这种机制创建出自己所写的子类实例。Objective-C中有一项强大的特性可以解决此问题,这就是『关联对象 Associated Object』。
可以给某对象关联许多其他对象,这些对象通过『键』来区分。存储对象值的时候,可以指明『存储策略 storage policy』,用以维护相应的"内存管理语义"。存储策略由名为objc-AssociationPolicy的枚举所定定义,包括下面的值,同事累出了与之等效的@property属性:假如关联对象成为了属性,那么它就会具备对应的语义。
关联类型 | 等效的属性@property |
---|---|
OBJC_ASSOCIATION_COPY | copy |
OBJC_ASSOCIATION_RETAIN | retain |
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | nonatomic, reatin |
OBJC_ASSOCIATION_COPY_NONATOMIC | nonatomic,copy |
下列方法可以管理关联对象:
// 此方法以给定的键和策略为某对象设置关联对象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
// 此方法根据给定的键从某对象中获取相应的关联对象值
id objc_getAssociatedObject(id object, const void *key)
// 此方法移除所指定对象的全部关联对象
void objc_removeAssociatedObjects(id object)
举例说明
______iOS开发中,之前经常用到UIAlertView类『现在已经过期』,该类提供了一种标准视图,可向用户展示警告信息。当用户按下按钮关闭该视图时,需要用委托协议(delegate protocol)来处理动作,但是,要想设置好这个委托机制,就得把创建警告视图和处理按钮动作的代码分开。由于代码分做两块,所以读起来不方便。比如说,我们之前使用UIAlertView时,一般会这么写:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"关联对象" message:@"添加一个Block" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"好的", nil];
[alertView show];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
[self cancelDeal];
}else{
[self doDeal];
}
}
____如果想在同一个类里,处理多个警告信息视图,那么代码就会变得更为复杂,我们必须在delegate方法中检查传入的alertView参数,并根据此选用相应的逻辑。要是能在创建警告视图的时候直接把处理每个按钮的逻辑都写好,那就简单多了。
____这可以通过关联对象来做,创建完警告视图之后,设定一个与之关联的"块 Block",等到执行delegate方法时再将其读出来。此方案的代码实现如下:
#import <objc/runtime.h>
static void * kAlertBlockKey = @"AlertBlockKey";
- (void)showAlert{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"关联对象" message:@"添加一个Block" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"好的", nil];
void (^touchAlertBlock)(NSInteger) = ^(NSInteger index) {
if (index == 0) {
[self cancelDeal];
}else{
[self doDeal];
}
};
objc_setAssociatedObject(alertView, kAlertBlockKey, touchAlertBlock, OBJC_ASSOCIATION_COPY);
[alertView show];
}
// UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
void (^alertBlock)(NSInteger) = objc_getAssociatedObject(alertView, kAlertBlockKey);
if (alertBlock) {
alertBlock(buttonIndex);
}
}
Tip
上面的例子只是一个最简单的应用,可以根据具体的需求实现自己想要的结果。