1、分类中添加关联属性
类中声明属性
@property(nonatomic,assign) int age;
相当于
@interface People : NSObject
{
int _age;
}
- (void)setAge:(int)age;
- (int)age;
@end
- (void)setAge:(int )age {
_age = age;
}
- (int)age{
return _age;
}
即:@property申明在类中:_age (对应的带下划线的成员变量) setter/getter的声明与实现
而分类中声明属性 :没有_age成员变量 ,没有gettersetter的实现;只有方法的声明,没有实现
@interface People : NSObject
- (void)setAge:(int)age;
- (int)age;
@end
分类不能直接声明成员变量 但是可以通过关联属性间接添加
证明
-
1、编译器报错
-
2、
struct _category_t { const char *name; struct _class_t *cls; const struct _method_list_t *instance_methods; const struct _method_list_t *class_methods; const struct _protocol_list_t *protocols; const struct _prop_list_t *properties; };
这里面根本有没有存放成员变量的位置;
思考:分类中@property声明的属性没有带下划线的成员变量,且只声明了getter/setter 怎么写出像系统那样的对象属性;
方法一:
重写setter/getter
通过全局字典 以对象的地址为key 值属性 的值为value
#import "WQPeople+Category.h"
NSMutableDictionary * totalDictionary;
@implementation WQPeople (Category)
+(void)load{
totalDictionary = [[NSMutableDictionary alloc] init];
}
- (NSString *)xfqName{
NSString *add = [NSString stringWithFormat:@"%p",self];
return totalDictionary[add];
}
- (void)setXfqName:(NSString *)xfqName{
NSString *add = [NSString stringWithFormat:@"%p",self];
totalDictionary[add] = xfqName;
}
WQPeople *people = [WQPeople alloc];
people.age = 10;
people.xfqName = @"20";
people.age = 10;
people.xfqName = @"20";
区别:
两者的存储是不一样的
age 是存储在类的结构体中 而xfqName 则是存储在字典中
struct WQPeople_IMP{
struct NSObject_IMP NSObject_IVARS;
int _age;
}
问题:
1、线程安全问题 需要加锁
2、如果再在分类中添加一个sex 属性需要重新添加一个字典 用来存sex
方法二:
#import "WQPeople+Category.h"
#import <objc/message.h>
static const void * xaabbbName = &xaabbbName;
//这里一定要赋值 因为const void * xaabbbName 得到的是 null 如果有两个属性那 都为null赋值 会出问题
@implementation WQPeople (Category)
-(NSString *)xfqName{
return @"";
}
-(void)setXfqName:(NSString *)xfqName{
objc_setAssociatedObject(self, xaabbbName, xfqName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
三:
#import "WQPeople+Category.h"
#import <objc/message.h>
static const void * xaabbbName ;
static const void * xaabbbName1 ;
/// 目前没发现 void * 有什么问题
//可以使用 这样只占用了一个字节
static const char * xaabbbName2 ;
@implementation WQPeople (Category)
-(NSString *)xfqName{
NSLog(@"xaabbbName===%s",xaabbbName);
return objc_getAssociatedObject(self, &xaabbbName);
}
-(void)setXfqName:(NSString *)xfqName{
// 第二个参数: const void * _Nonnull key 指针类型
///所以可以出入int *p
objc_setAssociatedObject(self, &xaabbbName, xfqName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
四:
#import "WQPeople+Category.h"
#import <objc/message.h>
@implementation WQPeople (Category)
-(NSString *)xfqName{
return objc_getAssociatedObject(self, @"xxfqname");
}
-(void)setXfqName:(NSString *)xfqName{
objc_setAssociatedObject(self, @"xxfqname", xfqName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
为什么可以使用@"xxfqname"这种字符串呢,因为这些东西都在全局常量区,全局唯一的
五:
#import "WQPeople+Category.h"
#import <objc/message.h>
@implementation WQPeople (Category)
-(NSString *)xfqName{
return objc_getAssociatedObject(self, @selector(xfqName));
}
-(void)setXfqName:(NSString *)xfqName{
objc_setAssociatedObject(self, @selector(xfqName), xfqName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
六:
#import "WQPeople+Category.h"
#import <objc/message.h>
@implementation WQPeople (Category)
-(NSString *)xfqName{
return objc_getAssociatedObject(self, _cmd));
}
-(void)setXfqName:(NSString *)xfqName{
objc_setAssociatedObject(self, @selector(xfqName), xfqName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
///_cmd = @selector(xfqName)
绑定的几种方式
1、
//static const void * nameKey = "nameKey";
//static const void * nameKey = &nameKey;
static const char nameKey ;
@implementation People (Test1)
#define NAMEKEY "name"
- (void)setName:(NSString *)name{
// objc_setAssociatedObject(<#id _Nonnull object#>, <#const void * _Nonnull key#>, <#id _Nullable value#>, <#objc_AssociationPolicy policy#>)
// objc_setAssociatedObject(self, nameKey, name, OBJC_ASSOCIATION_COPY);
// objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY);
// @"name" 字面量放在常量区 这样存取的时候都是同一个地址 可以使用宏
// objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY);
// objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name{
// return objc_getAssociatedObject(self, nameKey);
// return objc_getAssociatedObject(self, @"name");
// return objc_getAssociatedObject(self, @selector(name));
///这个只能在get中用 _cmd 相当于当前方法@selector(name) 而如果set中用那就是@selector(setName:) 不一致
return objc_getAssociatedObject(self, _cmd);
}
底层是如何存储的呢?
总结:
关联对象 并不是存储在被关联对象本身的内存中
而是被存储在AssociationsManager中
AssociationsManager 中存放着AssociationsHashMap 已对象的内存地址为key
以ObjectAssociationsMap 为value;
ObjectAssociationsMap 以入参key为key 以ObjectAssociation 对象为value
ObjectAssociation存放了value 和策略
简单理解就是 manager 中存放了map1 map1在存放了map2 map2中存放了ObjectAssociation 对象,对象中两个属性一个是value 一个是策略;
Map1 这层解决 不同对象的分类属性 比如people dog
map2这层解决 同一对象关联了多个不同的属性 比如xfqname xfqage
移除:
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
把value 设置为nil 此时会移除AssociationsMap 中的元素,相当于移除上面说的map2 移除的是不同key这一级别的
objc_removeAssociatedObjects(<#id _Nonnull object#>)
移除的是map1 对象级别的AssociationsHashMap的元素
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);//self = people
当对象people对象被释放的时候会自动移除AssociationsHashMap 里面的元素