Obj-C的已有类中,添加关联对象存放自定义数据

2016-12-02  本文已影响15人  奔跑吧小二

全文引自
《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

上面的例子只是一个最简单的应用,可以根据具体的需求实现自己想要的结果。

上一篇下一篇

猜你喜欢

热点阅读