博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

01-04 分类关联对象

Posted on 2020-11-23 21:55  肖无情  阅读(118)  评论(0编辑  收藏  举报

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 里面的元素