Category深度解析

因为Category在iOS平时的开发中用的比较多,所以本文从各个方面介绍一下Category,很多地方都是自己的理解,欢迎各位看到的同学批评指正,多多交流。

一、Category作用

  1. 为已存在的类添加方法;对类进行了很好的扩展,应对变化的需求。
  2. 可以把一个类的实现分散到多个文件中,使得每个文件不至于庞大,而且可以聚集同一逻辑的代码在一个文件中。同时,也可以按需加载方法。
  3. 同一个类可以由多个人共同完成。

二、为什么category不能添加属性或者说是成员变量

这个问题从runtime的角度进行分析。
下图是objc_class结构体:


classOfRuntime.png

不详细介绍每一个字段的含义了,主要介绍与category相关的部分:
在上面的objc_class结构体中,ivars是objc_ivar_list(成员变量列表)指针;methodLists是指向objc_method_list指针的指针。在Runtime中,objc_class结构体大小是固定的,不可能往这个结构体中添加数据,只能修改。所以ivars指向的是一个固定区域,只能修改成员变量值,不能增加成员变量个数。methodList是是一个二维数组,所以可以修改*methodLists的值来增加成员方法,虽没办法扩展methodLists指向的内存区域,却可以改变这个内存区域的值(存储的是指针)。因此,可以动态添加方法,不能添加成员变量。类似的是protocol,因为它是编译器处理的,所以可以添加变量。category是在运行时处理的。

三、怎么样可以实现添加属性或者说添加成员变量

上面说了category中不可能添加成员变量或属性。因为系统无法合成实现属性所需的实例变量,所以category中也无法添加@property。但有时候确实需要添加属性,那有没有其他办法,可以不改变对象的内存结构,变相的给对象增加成员变量。
我们可以Runtime的objc_getAssociatedObject和objc_setAssociatedObject方法来模拟属性的get和set方法,用关联对象来模拟实例变量,这样就有了@property的三要素,只是跟@property的实现机制是完全不一样的。
有两种方式,第一种代码更简洁:

.h文件:

@property (nonatomic) NSString Id;

.m文件,要#import <objc/runtime.h>

- (NSString *)Id
{
   return objc_getAssociatedObject(self, @selector(Id));
}

- (void)setId:(NSString *)Id
{
   objc_setAssociatedObject(self, @selector(Id), Id, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

第二种:

static void *UIViewPoint =@"UIViewPoint";
@implementation UIView (Point)

@dynamic pointView;

- (void)setPointView:(UIView *)pointView {
    objc_setAssociatedObject(self, UIViewPoint, pointView,OBJC_ASSOCIATION_RETAIN);
}

- (UIView *)pointView {
   return objc_getAssociatedObject(self, UIViewPoint);
}

这两个方法的原型如下:

id objc_getAssociatedObject(id object, const void *key);
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

@selector(Id) 是参数中的 key,方法二中使用了静态指针 static void * 类型的参数来代替,第一种方法因为省略了声明参数的代码,并且能很好地保证 key 的唯一性,所以更简洁。

OBJC_ASSOCIATION_RETAIN_NONATOMIC 从字面意思就能猜到它是和属性的修饰符是对应的。对应关系见如下的定义:

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                        *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                        *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                        *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                        *   The association is made atomically. */
};

当属性是基本数据类型的时候,可能只是需要添加get和set方法,不需要实例变量的时候,可以在category中这样添加属性,这种方式,同样没有改变对象的内存结构。

@property (nonatomic) CGFloat left;
   @property (nonatomic) CGFloat right;

- (CGFloat)left {
    return self.frame.origin.x;
}
- (void)setLeft:(CGFloat)x {
    CGRect frame = self.frame;
    frame.origin.x = x;
    self.frame = frame;
}
- (CGFloat)right {
    return self.left + self.width;
}

- (void)setRight:(CGFloat)right {
    if(right == self.right){
        return;
    }
    CGRect frame = self.frame;
    frame.origin.x = right - frame.size.width;
    self.frame = frame;
}

我查了很多资料,很多人认为category可以添加属性,但是不可以添加成员变量,我觉得这种说法是不严谨的,所以写了这篇博客,来总结一下。如果有理解的不对的地方,希望看到的朋友指正,大家多多交流。

参考:

  1. http://yulingtianxia.com/blog/2014/11/05/objective-c-runtime/
  2. http://zhangbuhuai.com/2015/04/26/unstanding-the-Objective-C-Runtime-part1/
  3. http://draveness.me/ao/

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    萌萌的小伟哥阅读 507评论 0 0
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 26,838评论 34 457
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 2,680评论 9 61
  • 文|张小鱼 如果不能住在你心里, 都是客死他乡。 ...
    张小鱼716阅读 81评论 1 2
  • 相信用过小米手机的同学都遇到一个问题,不管你是MIUI7还是内测中的MIUI8,即使你是很早的MIUI5、MIUI...
    科技达人15阅读 2,336评论 0 0