Category初探

来源:互联网 发布:女王级战列舰 知乎 编辑:程序博客网 时间:2024/05/21 00:16

      无论多么完美的设计终究抵不过新需求的无情!

      那么当我们遇到要向已经有的类添加方法,该怎么办呢?一般来说继承和分类(Category)都是很好的选择。今天我就一起研究一下分类。

目录

  1. category 与extension 的区别  
  2. category真面目

  3. category的方法覆盖

正文

          1.category 与extension 的区别

    extension可以为添加方法,看起来跟category功能差不多,但他们之间的差别很大。extension是 类的一部分,在编译期间就决定了它与类同生共死的属性。你必须知道一个类的实现文件,才能为它添加,遇到系统的,比如NSString,它就吃了鳖。而category就灵活多了,它的实现实在运行的时候。所以它无法为类添加实例变量,因为一个类的内存已经分配好了。            

          2.category真面目

     我们知道所有的类和对象在runtime层都是结构体表示的,category也不例外,category用结构体category_t(在objc-runtime-new.h中可以找到此定义),它包含了
     1)、类的名字(name)
     2)、类(cls)
     3)、category中所有给类添加的实例方法的列表(instanceMethods)
     4)、category中所有添加的类方法的列表(classMethods)
     5)、category实现的所有协议的列表(protocols)
     6)、category中添加的所有属性(instanceProperties)
typedef struct category_t {    const char *name;    classref_t cls;    struct method_list_t *instanceMethods;    struct method_list_t *classMethods;    struct protocol_list_t *protocols;    struct property_list_t *instanceProperties;} category_t;
   
     可以看出category可以是 类方法,实例方法,协议,属性,不可以是实例变量。下面我们写一个类来测试一下。

MyClass.h:
#import <Foundation/Foundation.h>@interface MyClass : NSObject- (void)printName;@end@interface MyClass(MyAddition)@property(nonatomic, copy) NSString *name;- (void)printName;@end
MyClass.m:
#import "MyClass.h"@implementation MyClass- (void)printName{    NSLog(@"%@",@"MyClass");}@end@implementation MyClass(MyAddition)- (void)printName{    NSLog(@"%@",@"MyAddition");}@end
   我们来看一下category编译之后的样子,使用clang命令:
clang -rewrite-objc MyClass.m
    会得到一个很大的文件,有10万多行,我们不管它,来看一下最后与category有关的代码
static struct /*_method_list_t*/ {unsigned int entsize;  // sizeof(struct _objc_method)unsigned int method_count;struct _objc_method method_list[1];} _OBJC_$_CATEGORY_INSTANCE_METHODS_MyClass_$_MyAddition __attribute__ ((used, section ("__DATA,__objc_const"))) = {sizeof(_objc_method),1,{{(struct objc_selector *)"printName", "v16@0:8", (void *)_I_MyClass_MyAddition_printName}}};static struct /*_prop_list_t*/ {unsigned int entsize;  // sizeof(struct _prop_t)unsigned int count_of_properties;struct _prop_t prop_list[1];} _OBJC_$_PROP_LIST_MyClass_$_MyAddition __attribute__ ((used, section ("__DATA,__objc_const"))) = {sizeof(_prop_t),1,{{"name","T@\"NSString\",C,N"}}};extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_MyClass;static struct _category_t _OBJC_$_CATEGORY_MyClass_$_MyAddition __attribute__ ((used, section ("__DATA,__objc_const"))) = {"MyClass",0, // &OBJC_CLASS_$_MyClass,(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_MyClass_$_MyAddition,0,0,(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_MyClass_$_MyAddition,};static void OBJC_CATEGORY_SETUP_$_MyClass_$_MyAddition(void ) {_OBJC_$_CATEGORY_MyClass_$_MyAddition.cls = &OBJC_CLASS_$_MyClass;}#pragma section(".objc_inithooks$B", long, read, write)__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CATEGORY_SETUP[] = {(void *)&OBJC_CATEGORY_SETUP_$_MyClass_$_MyAddition,};static struct _class_t *L_OBJC_LABEL_CLASS_$ [1] __attribute__((used, section ("__DATA, __objc_classlist,regular,no_dead_strip")))= {&OBJC_CLASS_$_MyClass,};static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {&_OBJC_$_CATEGORY_MyClass_$_MyAddition,};static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
</pre>  分析这段代码_OBJC_$_CATEGORY_INSTANCE_METHODS_MyClass_$_MyAddition 表示的是category里面的方法,method_count表示的是方法的个数,method_list表示方法结构体数组,结构体数组里有方法的名字,_OBJC_$_PRO_LIST_MyClass_$_MyAddition表示category里面的属性。_category_t _OBJC_$_CATEGORY_MyClass_$_MyAddition 这个结构体就是category了,还对应它的初始化。</div><h2>  3.<span style="color: rgb(42, 41, 53); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; line-height: 18px;"><span style="color: rgb(42, 41, 53); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; line-height: 18px;">category的方法覆盖</span></span></h2><div><span style="color: rgb(42, 41, 53); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; line-height: 18px;"><span style="color: rgb(42, 41, 53); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; line-height: 18px;"><span style="color: rgb(102, 102, 102); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; font-size: 13.92px; line-height: 24px;">鉴于上面几节我们已经把原理都讲了,这一节只有一个问题:</span><br style="padding: 0px; margin: 0px; color: rgb(102, 102, 102); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; font-size: 13.92px; line-height: 24px;" /><span style="color: rgb(102, 102, 102); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; font-size: 13.92px; line-height: 24px;">怎么调用到原来类中被category覆盖掉的方法?</span><br style="padding: 0px; margin: 0px; color: rgb(102, 102, 102); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; font-size: 13.92px; line-height: 24px;" /><span style="color: rgb(102, 102, 102); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; font-size: 13.92px; line-height: 24px;">对于这个问题,我们已经知道category其实并不是完全替换掉原来类的同名方法,只是category在方法列表的前面而已,所以我们只要顺着方法列表找到最后一个对应名字的方法,就可以调用原来类的方法:</span></span></span></div><div><span style="color: rgb(42, 41, 53); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; line-height: 18px;"><span style="color: rgb(42, 41, 53); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; line-height: 18px;"><span style="color: rgb(102, 102, 102); font-family: "Hiragino Sans GB", "Microsoft Yahei", 微软雅黑, sans-serif; font-size: 13.92px; line-height: 24px;"></span></span></span><pre name="code" class="objc">MyClass *class = [[MyClass alloc]init];    [class printName];        Class currentClass = [MyClass class];        MyClass *My = [[MyClass alloc]init];        if (currentClass) {        unsigned int methodCount;                Method *methodList = class_copyMethodList(currentClass, &methodCount);        IMP lastImp = NULL;        SEL lastSel = NULL;        for (NSInteger i = 0; i < methodCount; i++) {            Method method = methodList[i];                        NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(method)) encoding:NSUTF8StringEncoding];                        if ([@"printName" isEqualToString:methodName]) {                lastImp = method_getImplementation(method);                lastSel = method_getName(method);                            }        }                typedef void (*fn)(id, SEL);                        if (lastImp != NULL) {            fn f = (fn)lastImp;            f(My, lastSel);        }        free(methodList);    }    [class printName];
通过这段代码就可以调用到类里的方法。

   

    

 





 

 

    


       

0 0
原创粉丝点击