zoukankan      html  css  js  c++  java
  • 01-04 分类关联对象

    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 里面的元素
    
  • 相关阅读:
    Android学习笔记----天地图API开发之UnsatisfiedLinkError
    Android学习笔记----ArcGIS在线地图服务(Android API)坐标纠偏
    Android学习笔记----Java字符串MD5加密
    Android学习笔记----Java中的字符串比较
    MySQL数据库导入错误:ERROR 1064 (42000) 和 ERROR at line xx: Unknown command ''.
    新浪微博POI点签到数据及可视化的初步成果
    Chrome调试本地文件无法使用window.opener对象进行窗口间值传递
    132、Android安全机制(2) Android Permission权限控制机制(转载)
    用addOnGlobalLayoutListener获取View的宽高
    Android Studio解决unspecified on project app resolves to an APK archive which is not supported
  • 原文地址:https://www.cnblogs.com/xiaowuqing/p/14027379.html
Copyright © 2011-2022 走看看