美文网首页
分类Category添加属性实现

分类Category添加属性实现

作者: 54番茄 | 来源:发表于2018-03-29 11:55 被阅读36次

            Category不允许为已有的类添加新的成员变量,实际上允许添加属性的,因为即使你添加了@property (nonatomic,xxxx) ,它既不会生成实例变量,也不会生成setter、getter方法,所以你添加了也无法使用。
    那为什么category不能添加成员变量?
    看了一下网上的说法,了解一下,如下

    Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。它的定义如下:
    typedef struct objc_class *Class;
    
    objc_class结构体的定义如下:
    struct objc_class {
        Class isa  OBJC_ISA_AVAILABILITY;
    #if !__OBJC2__
        Class super_class                       OBJC2_UNAVAILABLE;  // 父类
        const char *name                        OBJC2_UNAVAILABLE;  // 类名
        long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
        long info                               OBJC2_UNAVAILABLE;  // 类信息,供运行期使用的一些位标识
        long instance_size                      OBJC2_UNAVAILABLE;  // 该类的实例变量大小
        struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 该类的成员变量链表
        struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定义的链表
        struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法缓存
        struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 协议链表
    #endif
    } OBJC2_UNAVAILABLE;
     在上面的objc_class结构体中,
    ivars是objc_ivar_list(成员变量列表)指针;
    methodLists是指向objc_method_list指针的指针。
    

    主要是这句话:在Runtime中,objc_class结构体大小是固定的,不可能往这个结构体中添加数据,只能修改。所以ivars指向的是一个固定区域,只能修改成员变量值,不能增加成员变量个数。methodList是一个二维数组,所以可以修改*methodLists的值来增加成员方法,虽没办法扩展methodLists指向的内存区域,却可以改变这个内存区域的值(存储的是指针)。因此,可以动态添加方法,不能添加成员变量。

    下面了做个Demo测试一下:

    1. 先创建一个NSArray的Category,准备给它加一个name的属性(当然这样做法是毫无意义的,它just只是举个例子啊),上段测试代码:


      .h中声明属性
    2. 然后在.m中调用


      运行到aa.name就直接炸了,找不到setter方法,同样的getter方法就不测试了都一样。

            要想实现,我们首先需要自己去添加setter、getter方法,直接在NSArray+Property.m文件里加就可以了,但是要真正添加可以使用的属性,还需要利用Runtime来关联对象,我们在setter方法里关联一个对象,在getter方法里获取对应key关联的对象。下面看实现:

    .h
    @interface NSArray (Property)
    @property (nonatomic,copy) NSString * name;
    -(void)testMethod;
    @end
    .m
    #import "NSArray+Property.h"
    #import <objc/runtime.h>
    //定义常量 必须是char类型,类似字典的key值,根据key存取值
    static char * KEY = "key";
    
    @implementation NSArray (Property)
    
    -(void)setName:(NSString *)name{
        /*
         objc_AssociationPolicy参数使用的策略:
         OBJC_ASSOCIATION_ASSIGN;            //assign策略
         OBJC_ASSOCIATION_COPY_NONATOMIC;    //copy策略
         OBJC_ASSOCIATION_RETAIN_NONATOMIC;  // retain策略
         
         OBJC_ASSOCIATION_RETAIN;
         OBJC_ASSOCIATION_COPY;
         */
        /*
         关联方法:
         objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
         
         参数:
         * id object 给哪个对象的属性赋值
           const void *key 属性对应的key
           id value  设置属性值为value
           objc_AssociationPolicy policy  使用的策略,是一个枚举值,和copy,retain,assign是一样的,手机开发一般都选择NONATOMIC
         */
        objc_setAssociatedObject(self, KEY, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    -(NSString*)name{
        return  objc_getAssociatedObject(self, KEY);
    }
    -(void)testMethod{
        NSLog(@"方法随便写");
    }
    @end
    
    ViewController中测试
    NSArray * aa =  [[NSArray alloc]initWithObjects:@"1",@"2", nil];
    aa.name = @"xixi";
    NSLog(@"aa有一个的name属性被赋值了,查看name值为 : %@",aa.name);
    [aa testMethod];
    
    **打印结果**
    输出: aa有一个的name属性被赋值了,查看name值为 : xixi
    输出: 方法随便写
    

    上面代码如果你不主动实现setter、getter方法,并用Runtime去关联的话是会报错找不到setter、getter方法,可以自己去试试。Runtime机制其实还有很多方便用处,请查看 Runtime的常用方式及实现

    哦,还有点儿要注意了,如果有两个分类,他们都实现了相同的方法,如何判断谁先执行?
    1、在本类和分类有相同的方法时,优先调用分类的方法再调用本类的方法。
    2、分类之间的执行顺序,可以通过targets -> Build Phases -> Complie Source进行调节,注意执行顺序是从上到下的。(有两个相同方法名的分类)

    再追加点儿对类扩展(extension)说明:
    extension和Category不同,类扩展即可以声明成员变量又可以声明方法,但是没有像Category有.m文件。extension听上去很复杂,但其实我们很早就认识它了。
    你记得继承自UIViewController的ViewController和继承自NSObject的类有什么不同么?
    继承自UIViewController的ViewController类有这个

     @interface ViewController()  //这个玩意儿,就是类扩展的写法
     @end
    

    类扩展可以定义在.m文件中,这种扩展方式中定义的变量都是私有的,也可以定义在.h文件中,这样定义的代码就是共有的,类扩展在.m文件中声明私有方法是非常好的方式。
    参考文章:Objective-C的Category与关联对象实现原理

    相关文章

      网友评论

          本文标题:分类Category添加属性实现

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