美文网首页iOS面试汇总
iOS 分类(Categories)

iOS 分类(Categories)

作者: iOS_肖晨 | 来源:发表于2017-10-11 17:28 被阅读58次

    简介:

    分类是一种为现有的类添加新方法的方式。
    利用Objective-C的动态运行时分配机制,可以为现有的类添加新方法,这种为现有的类添加新方法的方式称为分类,他可以为任何类添加新的方法,包括那些没有源代码的类。
    分类使得无需创建对象类的子类就能完成同样的工作。

    我们知道,所有的OC类和对象,在runtime层都是用struct表示的,category也不例外,在runtime层,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;
    

    主要作用:

    1. 将类的实现分散到多个不同文件或多个不同框架中。
    2. 创建对私有方法的前向引用。
    3. 向对象添加非正式协议。

    OC语法中,可以对类的实例变量加@private/@public等关键字进行修饰。但是对于类的方法只分+开头的类方法和-开头的对象方法,不能对一个类的方法加@private这样的关键字进行限定,那么OC中如何定义私有方法呢?

    答案是:定义在类名.h文件中的方法/属性一定是公开的,而在类名.m中的类延展(Extension)中定义的方法/属性都是私有的。或者不在任何地方申明,只在类.m中写实现代码的方法也是私有的。

    Cocoa没有任何真正的私有方法。只要知道对象支持的某个方法的名称,即使该对象所在的类的接口中没有该方法的声明,你也可以调用该方法。不过这么做编译器会报错,但是只要新建一个该类的类别,在类别.h文件中写上原始类该方法的声明,类别.m文件中什么也不写,就可以正常调用私有方法了。这就是传说中的私有方法前向引用。 所以说cocoa没有真正的私有方法。

    使用Category需要注意的点:

    1. Category的方法不一定非要在@implementation中实现,也可以在其他位置实现,但是当调用Category的方法时,依据继承树没有找到该方法的实现,程序则会崩溃。
    2. Category理论上不能添加变量,但是可以使用@dynamic 来弥补这种不足。
    static const void * externVariableKey = &externVariableKey;
    @implementation NSObject (Category)
    @dynamic variable;
    - (id) variable
    {
        return objc_getAssociatedObject(self, externVariableKey);
    }
    - (void)setVariable:(id) variable
    {
        objc_setAssociatedObject(self, externVariableKey, variable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    1. category的方法没有“完全替换掉”原来类已经有的方法,也就是说如果category和原来类都有methodA,那么category附加完成之后,类的方法列表里会有两个methodA。
    2. category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休,殊不知后面可能还有一样名字的方法。
    // 备注:Person 类及他的分类都有一个 printName 方法,由于之前的原因,直接调用 printName 方法会直接调用分类的实现,所以我们需要在 methodList 中找到后面的实现,然后调用对应的方法。
    // 获取 Person 类本身的 printName 方法
    unsigned int methodCount;
    Method *methodList = class_copyMethodList([Person class], &methodCount);
    IMP lastImp = NULL;
    SEL lastSel = NULL;
    for (NSUInteger 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(*imp)(id, SEL);
    if (lastImp != NULL) {
        imp f = (imp)lastImp;
        f(p, lastSel);
    }
    
    free(methodList);
    

    相关文章

      网友评论

        本文标题:iOS 分类(Categories)

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