runtime总结(中)

来源:互联网 发布:linux基础试题选择题 编辑:程序博客网 时间:2024/06/03 11:23

参考链接
Objective-C Runtime 运行时之二:成员变量与属性
OC 中runtime的理解 以及 使用


什么是关联对象
小马在面试的时候,遇到这样一个问题:“如何給NSArray添加一个属性(不能使用继承)”,小马立马蒙逼了,不能用继承,难道用分类?但是分类貌似只能添加方法不能添加属性啊,小马百思不得其解,直到后来接触到了runtime才恍然大悟。
什么是关联对象
关联对象是指某个OC对象通过一个唯一的key连接到一个类的实例上。
举个例子:xiaoming是Person类的一个实例,他的dog(一个OC对象)通过一根绳子(key)被他牵着散步,这可以说xiaoming和dog是关联起来的,当然xiaoming可以牵着多个dog。
怎样关联对象

//关联对象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)
id object:被关联的对象(如xiaoming)const void *key:关联的key,要求唯一id value:关联的对象(如dog)objc_AssociationPolicy policy:内存管理的策略

我们可以把关联对象想象成一个Objective-C对象(如字典),这个对象通过给定的key连接到类的一个实例上。不过由于使用的是C接口,所以key是一个void指针(const void *)。我们还需要指定一个内存管理策略,以告诉Runtime如何管理这个对象的内存。这个内存管理的策略可以由以下值指定:

OBJC_ASSOCIATION_ASSIGNOBJC_ASSOCIATION_RETAIN_NONATOMICOBJC_ASSOCIATION_COPY_NONATOMICOBJC_ASSOCIATION_RETAINOBJC_ASSOCIATION_COPY

当宿主对象被释放时,会根据指定的内存管理策略来处理关联对象。如果指定的策略是assign,则宿主释放时,关联对象不会被释放;而如果指定的是retain或者是copy,则宿主释放时,关联对象会被释放。我们甚至可以选择是否是自动retain/copy。当我们需要在多个线程中处理访问关联对象的多线程代码时,这就非常有用了。

我们将一个对象连接到其它对象所需要做的就是下面两行代码:

static char myKey;objc_setAssociatedObject(self, &myKey, anObject, OBJC_ASSOCIATION_RETAIN);

在这种情况下,self对象将获取一个新的关联的对象anObject,且内存管理策略是自动retain关联对象,当self对象释放时,会自动release关联对象。另外,如果我们使用同一个key来关联另外一个对象时,也会自动释放之前关联的对象,这种情况下,先前的关联对象会被妥善地处理掉,并且新的对象会使用它的内存。

id anObject = objc_getAssociatedObject(self, &myKey);

我们可以使用objc_removeAssociatedObjects函数来移除一个关联对象,或者使用objc_setAssociatedObject函数将key指定的关联对象设置为nil。值得注意的是,我们不需要主动调用removeAssociated来接触关联的对象,如果需要解除指定的对象,可以使用setAssociatedObject置nil来实现。因为调用removeAssociated的时候你不知道这个person到底牵了多少只dog。

关联对象的应用
给分类添加属性

@interface Dog (AddProperty)@property (nonatomic,copy)NSString *master;@end
static char *associatedObjectKey;@implementation Dog (AddProperty)- (NSString *)master {//    return objc_getAssociatedObject(self, _cmd);    return objc_getAssociatedObject(self, associatedObjectKey);;}
- (void)setMaster:(NSString *)master {    //设置关联对象//    objc_setAssociatedObject(self, @selector(master), master, OBJC_ASSOCIATION_RETAIN_NONATOMIC);    objc_setAssociatedObject(self, associatedObjectKey, master, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //获取关联对象}

注意:我们也可以把方法的地址作为key,_cmd代表当前调用方法的地址。

我们也可以把一个对象绑定到任何对象上,假定我们想要动态地将一个Tap手势操作连接到任何UIView中,并且根据需要指定点击后的实际操作。这时候我们就可以将一个手势对象及操作的block对象关联到我们的UIView对象中。这项任务分两部分。首先,如果需要,我们要创建一个手势识别对象并将它及block做为关联对象。如下代码所示

- (void)setTapActionWithBlock:(void (^)(void))block{    UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kDTActionHandlerTapGestureKey);    if (!gesture)    {        gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];        [self addGestureRecognizer:gesture];        objc_setAssociatedObject(self, &kDTActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);    }    objc_setAssociatedObject(self, &kDTActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);}

这段代码检测了手势识别的关联对象。如果没有,则创建并建立关联关系。同时,将传入的块对象连接到指定的key上。注意block对象的关联内存管理策略。
手势识别对象需要一个targetaction,所以接下来我们定义处理方法:

- (void)__handleActionForTapGesture:(UITapGestureRecognizer *)gesture{    if (gesture.state == UIGestureRecognizerStateRecognized)    {        void(^action)(void) = objc_getAssociatedObject(self, &kDTActionHandlerTapBlockKey);        if (action)        {            action();        }    }}

我们需要检测手势识别对象的状态,因为我们只需要在点击手势被识别出来时才执行操作。

从上面的例子我们可以看到,关联对象使用起来并不复杂。它让我们可以动态地增强类现有的功能。我们可以在实际编码中灵活地运用这一特性。

0 0
原创粉丝点击