分类Category添加属性实现

        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中调用


    分类Category添加属性实现_第1张图片
    运行到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 
//定义常量 必须是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添加属性实现)