在Category中可以添加属性吗?

Category直接添加属性使用会报错

类目和属性(Category and Property)

关于property,有两个关键字@synthesize和@dynamic:

@synthesize: 实现property所声明的方法的定义。

property 声明方法 ->头文件中申明getter和setter方法 synthesize定义方法 -> m文件中实现getter和setter方法。 如果我们没有手动的实现property的setter和getter方法,编译器会自动的添加上这两个方法。

@dynamic:
告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成。

在平常我们都习惯了不写这两个关键词,这个时候其实默认是 @synthesize var = _var

我直接在ViewController (extension)中定义一个name属性。不使用它的时候运行完好。一使用它的时运行就会报错:

[ViewController setName:]: unrecognized selector sent to instance 0x7f9473705540。
而且编译器直接就提供警告:

Property ‘name’ requires method ‘setName:’ to be defined - use @dynamic or provide a method implementation in this category。
意思是在这个类中,属性name的setter的定义方法,用@dynamic 或者提供一个方法来实现。

然后我们在implementation中添加 @dynamic name;这样警告消失,但是运行依然会报 unrecognized selector sent to instance xxx的错误。

然后我们在 implementation 中实现它的getter和setter方法,也会直接报错 undeciarediared identifier XX

1

结论

1.类目(category)可以添加属性,但不能实现部分的@synthesize。

  1. 类目中可以实现setter和getter方法,但是不存在_name的成员变量。

解决方法

这个时候应该怎么办呢?我们知道Obj-c是一门动态语言,强大的运行时(Runtime)机制能够为类关联引用,然后通过这种方法来实现给类别添加属性。其实就是动态绑定。

属性添加和检索关联

首先需要导入Runtime相关的头文件
#import <objc/runtime.h>

//这个方法时为对象object添加key指定的地址作为关键字,以value为值关联引用,第四个参数pilicy指的是关联引用的存储策略。 //通过将value设置为nil 可以删除key的关联 OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);

//返回object以key为关键字关联的对象,如果没有,则反火nil OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);

如果我们需要为一个类别的多个属性关联引用,所以我们需要用不同的key值来区别。
而且键值必须要使用一个确定且不可变的地址。所以选择定义在实现文件的static静态局部变量的地址。
policy策略就跟定义property的时候才用的存储关键字相似了。

关联的policy策略

objc_AssociationPolicy policy:enum {

//五种类型
OBJC_ASSOCIATION_ASSIGN = 0, //关联对象的属性是弱引用 weak

OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //关联对象的属性是强引用并且关联对象不使用原子性 strong

OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //关联对象的属性是copy并且关联对象不使用原子性 copy

OBJC_ASSOCIATION_RETAIN = 01401, //关联对象的属性是retain并且关联对象使用原子性 strong copy

OBJC_ASSOCIATION_COPY = 01403 //关联对象的属性是copy并且关联对象使用原子性 copy atomic
};

断开关联

runtime 提供了断开关联的函数。但是更喜欢使用传入nil来断开关联,会更佳安全,毕竟,我不敢保证是不是有其他地方会使用到已经关联的对象。而且当category中添加多个属性的时候可以单个断开关联。

OBJC_EXPORT void objc_removeAssociatedObjects(id object) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);

代码实现

Category属性检索关联(动态绑定)

1、 导入头文件

#import <objc/runtime.h>

2、 定义用作键值的静态变量

//static char *nameKey = “nameKey”;也行

static const char *nameKey = “nameKey”;

3、定义存取方法

-(NSString *)name{
return objc_getAssociatedObject(self, nameKey);
}

-(void)setName:(NSString *)name{
objc_setAssociatedObject(self, nameKey, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

使用运行

首先在category中定义两个关联属性

在vievcontroller中也定义了两个属性作为试验

然后用objc_removeAssociatedObjects(self);断开关联

然后输出结果,category中关联的属性两个都输出null而本身的属性正常输出。

总结

通过简单的验证我的发现
1、objc_removeAssociatedObjects(id object)是断开所有关联。
2、使用runtime给对象关联的属性跟本身的属性本质是是不一样的,可使用却是完全一样的。