runtime - 关联属性

2016-07-05  本文已影响68人  啊啊啊啊锋

我们知道苹果不允许我们自己给已经存在的类通过分类添加方法的,
但是有时候我们确实需要给某个类从而分类添加属性,那么我们该怎么办呢?
通过这一节学习,我们也许就会有思路了!

为了引出今天要学习的东西,假设我们的项目有这样一个需求,我们要给所有继承自NSObjct的类增加一个objName的属性,而且这个属性必须卸载分类里边,好了那么在我们还不是太了解runtime的情况下,我们可能会这么做:
新建一个NSObjct的分类,命名为ObjConnect,在.h文件里边,我们声明一个属性:

/** 我么知道如果我们给自定义的分类增加了某个属性是没问题的,但是如果是给某个分类增加一个属性那么会有什么问题呢?
 *  在这里我们只在NSObjct的分类里边增加一个objName的属性,而不做任何其他事情,那么我们看看会有什么问题 
 */
@property (nonatomic, copy) NSString *objName;

然后我们再新建一个Dog类,继承自NSObjct,除此之外我们不做任何其他事情。

在我们的控制器里边,初始化一个Dog对象,然后给objName初始化一个值并打印之,代码如下:

Dog *dog = [Dog new];
dog.objName = @"旺财";
NSLog(@"%@", dog.objName);

好了,让我们运行程序:

2016-07-05 14:44:06.699 ZFRuntime[1314:135570] -[Dog setObjName:]: unrecognized selector sent to instance 0x7fbed341c7b0
2016-07-05 14:44:06.703 ZFRuntime[1314:135570] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Dog setObjName:]: unrecognized selector sent to instance 0x7fbed341c7b0'

这时候我们看到程序崩溃了,并且控制台输出了如上的错误,提示[Dog setObjName:]这个方法系统并不认识,因为程序不允许我们给分类添加这样的属性。

下边我们在思考,既然提示[Dog setObjName:]这个方法认识,那么我们自己写objName的setget方法行不行呢?按照这个思路我们再来试一下,在NSObjct这个分类里边增如下几行代码:

/** 我们看看自己来写`objName`的`set`和`get`方法 */
- (void)setObjName:(NSString *)objName
{
    self.objName = objName;
}

- (NSString *)objName
{
    return self.objName;
}

然后我们再次运行程序:这个时候程序出现了一个更严重的问题,提示内存出现泄漏,看来这样也打不到我们的目的。

既然上边两个思路都行不通,那么让我们用运行时的方式看看能不能解决这个问题呢?

首先在NSObjct分类的.m文件里边定义一个关联值:

/** 系统将会通过这个关联值来获取属性值,注意必须保证这个值唯一,通常使用`static const void *`方式来声明 */
static const void *TagSetObjNameKey;

然后实现在两个方法:

/** 设置关联的值,也就是设置`objName`属性的值,可以理解为这个属性的`setter`方法 */
- (void)setObjName:(NSString *)objName
{
    objc_setAssociatedObject(self, TagSetObjNameKey, objName, OBJC_ASSOCIATION_COPY);
}

/** 获取关联的值,也就是获取`objName`,可以理解为这个属性的`getter`方法 */
- (NSString *)objName
{
    return objc_getAssociatedObject(self, TagSetObjNameKey);
}

好了让我们再次运行程序,这是口可以看到控制台打印信息:

2016-07-05 15:01:26.036 ZFRuntime[1424:145877] 旺财

说明我们的目的已经实现了,通过runtime的方法,我们成功地给分类增加了一个属性!

既然运行时的这个API这么方便,那么再来分析下这几个方法到底什么意思,我们以后要是使用的话应该传递什么参数呢?

苹果官方文档里边,和属性关联相关的API有三个,这三个API分别是:

/**
*  定义:通过给定关联的`类`和`关联策略`等,为某个类增加关联(属性)
*
*  @param object 要关联的(要增加属性的)那个类
*  @param key    关联的键,例如上边例子中我们传递的是 `TagSetObjNameKey`
*  @param value  通过`键`要关联的那个值,我们简单可以理解为我们自定义的这个属性。(如果要清除某个关联,这个值可以设置为nil,但是很少这么做)
*  @param policy 关联策略(关于这个参数下边我们会详解)
*/
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)

/**
*  定义:通过给定`类`和`关联键`获取关联(属性)
*
*  @param object 同上
*  @param key    同上
*
*  @return 关联的值(属性)
*/
id objc_getAssociatedObject(id object, void *key)

/**
*  定义:给定关联的`类`,移除所有关联(这个方法)
*  备注:这个方法很少用
*  @param object 同上
*/
void objc_removeAssociatedObjects(id object)

这里有几点我们有必要说明一下:

上一篇下一篇

猜你喜欢

热点阅读