关联

2017-07-19  本文已影响11人  流浪瑞兹

开篇

可能有人会告诉你分类是不能添加属性的,分类真的不能添加属性吗?请往下看

创建分类:

@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的良心所在:

它告诉你你要实现settergetter方法,虽然ivars链表不能扩展,但是methodLists可以啊。但是之前的settergetter都是结合实例变量实现的,现在该咋办呢?
比比了这么多,那么现在有请今天的🐽脚登场:

/*
* 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取到对应实例变量,它只是有对应的settergetter方法而已。

代码封装

#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

 */

上一篇 下一篇

猜你喜欢

热点阅读