开篇
可能有人会告诉你分类是不能添加属性的,分类真的不能添加属性吗?请往下看
创建分类:
@interface Person (Character)
@property(nonatomic,strong)NSString* name;
@end
@implementation Person (Character)
@end
常规调用:
Person* person = [[Person alloc] init];
person.name = @"陈二狗";
结果GG:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person setName:]: unrecognized selector sent to instance 0x60000000c6f0'
沃日你哥 咋不行呢 。别着急
我们做如下操作:
- 我们创建一个分类,并在分类中声明一个实例变量:

会发现有报错,系统告诉你在分类中不能添加实例变量,也就是说,Person
类的结构体中的实例变量链表(ivars
)不可扩展,OC不支持往已存在的类中添加实例变量,因此不管是系统库提供的类,还是我们自己定义的类,都无法动态添加成员变量。有人可能会说,系统有个class_addIvar
方法啊,怎么不行? 这个方法的用处是:我们通过运行时来创建一个类时,我们才可以使用class_addIvar
函数添加实例变量。但是这个方法也只能在objc_allocateClassPair
函数与objc_registerClassPair
之间调用。另外,这个类不能是元类。
那么分类中添加实例变量是不可能了,还是沿着属性这条路走下去吧。我们到.m
中查看,会发现Xcode的良心所在:

它告诉你你要实现setter
和getter
方法,虽然ivars
链表不能扩展,但是methodLists
可以啊。但是之前的setter
和getter
都是结合实例变量实现的,现在该咋办呢?
比比了这么多,那么现在有请今天的🐽脚登场:
/*
* object 源对象
* key 关键字
* value 被关联的对象
* policy 关联策略
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
上面的方法用来把一个对象与另一个对象进行关联,并不会像属性自动生成的实例变量一样在当前类开辟空间。
于是我们可以这样写:
-(NSString *)name {
return objc_getAssociatedObject(self, @"name");
}
-(void)setName:(NSString *)name {
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
注意,这样的属性是不会生成实例变量的,不要总想用_name
取到对应实例变量,它只是有对应的setter
和getter
方法而已。
代码封装
#import <objc/runtime.h>
// 添加id类型属性
#define ASSOCIATED(propertyName, setter, type, objc_AssociationPolicy)\
- (type)propertyName {\
return objc_getAssociatedObject(self, _cmd);\
}\
\
- (void)setter:(type)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), object, objc_AssociationPolicy);\
}
// 添加BOOL类型属性
#define ASSOCIATED_BOOL(propertyName, setter)\
- (BOOL)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.boolValue;\
}\
\
- (void)setter:(BOOL)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}
// 添加NSInteger类型属性
#define ASSOCIATED_NSInteger(propertyName, setter)\
- (NSInteger)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.integerValue;\
}\
\
- (void)setter:(NSInteger)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}
// 添加float类型属性
#define ASSOCIATED_float(propertyName, setter)\
- (float)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.floatValue;\
}\
\
- (void)setter:(float)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}
// 添加double类型属性
#define ASSOCIATED_double(propertyName, setter)\
- (double)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.doubleValue;\
}\
\
- (void)setter:(double)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}
// 添加long long类型属性
#define ASSOCIATED_longlong(propertyName, setter)\
- (long long)propertyName {\
NSNumber *value = objc_getAssociatedObject(self, _cmd); return value.longLongValue;\
}\
\
- (void)setter:(long long)object\
{\
objc_setAssociatedObject(self, @selector(propertyName), @(object), OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
}
//在类别中添加属性
//使用方法如下:
/**
.h
#import <Foundation/Foundation.h>
@interface Person (AssociatedTest)
@property (nonatomic, strong) NSString *name;
@property (nonatomic, weak) id delegate;
@property (nonatomic, assign) BOOL isOK;
@end
.m
#import "Person + AssociatedTest.h"
@implementation NSObject (AssociatedTest)
ASSOCIATED(name, setName, NSString *, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
ASSOCIATED(delegate, setDelegate, id, OBJC_ASSOCIATION_ASSIGN)
ASSOCIATED_BOOL(isOK, setIsOK)
@end
*/
网友评论