iOS分类不能添加属性原因的探索

作者: 星月之星歌 | 来源:发表于2016-10-28 13:53 被阅读3532次

    最近跟人交流时,提到一个问题,说iOS分类中不能添加属性。这里探讨一下不能添加的原因和添加的方法。
    首先,创建一个person类,代码如下:

    XGPerson.h

    #import <Foundation/Foundation.h>
    
    @interface XGPerson : NSObject
    /// 年龄
    @property (nonatomic, copy) NSString *age;
    /// 性别
    @property (nonatomic, copy) NSString *sex;
    
    - (void)text1;
    
    @end
    
    

    XGPerson.m

    #import "XGPerson.h"
    
    @implementation XGPerson
    
    - (void)text1 {
        NSLog(@"%s",__func__);
    }
    
    - (void)text2 {
        NSLog(@"%s",__func__);
    }
    @end
    
    

    在控制器里获取并打印该类的成员变量、属性和方法,代码如下:

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // 获取成员变量
        unsigned int ivarCount = 0;
        Ivar *ivars = class_copyIvarList([XGPerson class], &ivarCount);
        for (int i = 0; i < ivarCount; i++) {
            Ivar ivar = ivars[i];
            NSLog(@"第%d个成员变量:%s",i,ivar_getName(ivar));
        }
        free(ivars);
        // 获取属性
        unsigned int propertyCount = 0;
        objc_property_t *propertyList = class_copyPropertyList([XGPerson class], &propertyCount);
        for (int i = 0; i < propertyCount; i++) {
            objc_property_t property = propertyList[i];
            NSLog(@"第%d个属性:%s",i,property_getName(property));
        }
        
        
        // 获取方法列表
        unsigned int methodCount = 0;
        Method *methods = class_copyMethodList([XGPerson class], &methodCount);
        for (int i = 0; i < methodCount; i++) {
            Method method = methods[i];
            
            NSLog(@"第%d个方法:%s",i, sel_getName(method_getName(method)));
        }
        
    }
    

    此时控制台输出如下:

    没有分类时.png

    这里需要提出的是,平时使用@property的时候,系统会自动生成带“_”的成员变量和该变量的setter和getter方法。也就是说,属性相当于一个成员变量加getter和setter方法。那么,在分类里使用@property会是什么样子呢,下面来创建一个分类:
    XGPerson+height.h

    #import "XGPerson.h"
    
    @interface XGPerson (height)
    
    @property (nonatomic, copy) NSString *height;
    
    @end
    

    XGPerson+height.m

    #import "XGPerson+height.h"
    
    #import <objc/runtime.h>
    
    @implementation XGPerson (height)
    
    
    @end
    

    如果像上面一样只在.h文件里声明height,那么.m文件里会出现两个警告,意思是说没有实现setter和getter方法。


    警告.png

    此时在控制器里执行touchesBegan方法,控制台输出如下:


    有分类未实现存取方法.png

    可以看到,此时person类里并没有添加带“_”的成员变量,也没有实现setter和getter方法,只是在属性列表里添加了height属性。并且此时如果在控制器里调用self.height,程序运行时会报错,显示找不到该方法。实现一下person分类里的两个方法:
    XGPerson+height.m

    #import "XGPerson+height.h"
    
    #import <objc/runtime.h>
    
    @implementation XGPerson (height)
    
     - (NSString *)height {
    
     }
     
     - (void)setHeight:(NSString *)height {
    
     }
    
    @end
    

    此时在控制器里执行touchesBegan方法,控制台输出如下:


    有分类实现存取方法.png

    可以看到即使实现了setter和getter方法,也仍然没有添加带“”的成员变量,也就是说,在setter和getter方法里仍然不能直接访问以下划线开头的成员变量,因为在分类里用@property声明属性时系统并没有添加以“”开头的成员变量。此时要达到添加的目的可以使用运行时的关联对象。示例代码如下:
    XGPerson+height.m

    #import "XGPerson+height.h"
    
    #import <objc/runtime.h>
    
    @implementation XGPerson (height)
    
    
    
     - (NSString *)height {
     return objc_getAssociatedObject(self, @"height");
     }
     
     
     - (void)setHeight:(NSString *)height {
     objc_setAssociatedObject(self, @"height", height, OBJC_ASSOCIATION_COPY_NONATOMIC);
     }
    
    @end
    

    当然也可以在setter和getter方法里访问该类其他的属性,比如在UIView的分类的里添加x、y属性,可以直接返回self.frame.origin.x和self.frame.origin.y。

    总结

    在分类里使用@property声明属性,只是将该属性添加到该类的属性列表,并声明了setter和getter方法,但是没有生成相应的成员变量,也没有实现setter和getter方法。所以说分类不能添加属性。但是在分类里使用@property声明属性后,又实现了setter和getter方法,那么在这个类以外可以正常通过点语法给该属性赋值和取值。就是说,在分类里使用@property声明属性,又实现了setter和getter方法后,可以认为给这个类添加上了属性。

    相关文章

      网友评论

      • 麦子_KB:性别为什么喜欢用sex,而不是gender
      • asaBoat:扩展里面的属性声明是和类的属性功能一样的,而分类只能通过动态关联对象的方法给外界封装一个假的实例变量,事实上这个变量在类结构中不存在。
      • PGOne爱吃饺子:楼主 你好 ,后面你说的这句话是不是有问题
        “在分类里使用@property声明属性,只是将该属性添加到该类的属性列表,并声明了setter和getter方法,但是没有生成相应的成员变量,也没有实现setter和getter方法。所以说分类不能添加属性。但是在分类里使用@property声明属性后,又实现了setter和getter方法,那么在这个类以外可以正常通过点语法给该属性赋值和取值。就是说,在分类里使用@property声明属性,又实现了setter和getter方法后,可以认为给这个类添加上了属性。
        麻辣香锅加特辣:@rayZhou 声明了setter和getter方法,但没有实现,方法列表没有这两个方法吗
        rayZhou:没有问题,哪里有问题,博主写的很好!可以认为 ,并不是真的添加了属性 ,分类并不是一个对象 ,再关联的时候 ,需要给一个唯一的静态地址,和对应的值
      • 李某lkb:直接声明一个@synthesize属性可以吗?
      • dadahua:一句话就是 分类 不会生成 带下划线 的 成员变量 和 set get 方法。
        dadahua:首先感谢作者的文章,这里有更详细的原理:https://www.zhihu.com/question/51513146/answer/126443097
      • itclimb:对分类有了进一步了解,赞
      • 柳絮风微:您好,请问一下为什么我用objc_getAssociatedObject(),这个方法会报错的?
        没办法实现getter和setter方法啊!
      • 618e89071db3:有所帮助 mark

      本文标题:iOS分类不能添加属性原因的探索

      本文链接:https://www.haomeiwen.com/subject/ogxeuttx.html