美文网首页
iOS:Category浅析

iOS:Category浅析

作者: 春暖花已开 | 来源:发表于2019-04-22 15:44 被阅读0次
    说明 时间
    首次发布 2019年04月22日
    最近更新 2019年09月07日
    一、分类的加载处理过程

    结论:runtime加载某个类的所有分类数据,将所有category的方法、属性、协议合并到一个大数组里(后面参与编译的分类数据,会插入到数组的前面),最后将合并后的分类数据插入到类原来位置的前面

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    //本类
    @interface SuperMan : NSObject
    @end
    
    //分类
    @interface SuperMan (Fly)
    
    - (void)fly;
    
    + (void)eat;
    
    @end
    NS_ASSUME_NONNULL_END
    ========================
    
    //.m
    #import "SuperMan.h"
    
    @implementation SuperMan
    
    - (void)fly {
        NSLog(@"fly");
    }
    
    + (void)eat {
        NSLog(@"eat");
    }
    
    @end
    ========================
    
    @implementation SuperMan (Fly)
    
    - (void)fly {
        NSLog(@"fly");
    }
    
    + (void)eat {
        NSLog(@"eat");
    }
    
    @end
    

    打印本类里的方法

    #import "ViewController.h"
    
    #import "SuperMan.h"
    #import <objc/runtime.h>
    
    void getAllMethodsFor(Class cls) {
        
        unsigned int count;
        Method *methods = class_copyMethodList(cls, &count);
        NSMutableArray *list = [NSMutableArray array];
        for (unsigned int i = 0; i < count; i++) {
            Method method = methods[i];
            [list addObject:NSStringFromSelector(method_getName(method))];
        }
        
        //释放内存
        free(methods);
        
        NSString *methodName = [list componentsJoinedByString:@", "];
        NSLog(@"%@", methodName);
    }
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //通过类对象,获取到成员方法
        getAllMethodsFor(SuperMan.class);
        //通过元类,获取到本类的类方法
        getAllMethodsFor(object_getClass(SuperMan.class));
    }
    
    @end
    

    输出:

    fly, fly
    eat, eat
    

    从中,我们可以看到里面有两个flyeat方法,并没有发生我们所认为的方法覆盖。结合上面我们所说的将分类数据插入到类原来位置的前面,所以当真正调用的时候,找到前面的方法之后,就不会再接着往后便利,于是就发生了“分类的方法覆盖了本来的实现”。


    二、系统调用+load方法的顺序探究

    代码结构:Cat继承自Animal,并且二者皆有一个分类,每个文件都有个一个+load方法。

    //Animal
    @interface Animal : NSObject
    @end
    
    #import "Animal.h"
    
    @implementation Animal
    
    + (void)load {
        NSLog(@"====Animal");
    }
    
    @end
    ========================
    
    //Cat
    @interface Cat : Animal
    @end
    
    @implementation Cat
    
    + (void)load {
        NSLog(@"====Cat");
    }
    
    @end
    ========================
    
    //Animal+Test
    @interface Animal (Test)
    @end
    
    @implementation Animal (Test)
    
    + (void)load {
        NSLog(@"====Animal+Test");
    }
    
    @end
    ========================
    
    //Cat+Test
    @interface Cat (Test)
    @end
    
    @implementation Cat (Test)
    
    + (void)load {
        NSLog(@"====Cat+Test");
    }
    
    @end
    

    结论1:无论怎么调整Animal、Cat、Animal+Test和Cat+Test的顺序,始终先打印Animal、Cat,即:先加载本类,再加载分类。Animal+Test和Cat+Test的顺序,则是编译顺序决定打印顺序,谁在前,就先调用谁的+load方法。

    如果再增加一个继承自NSObject的Dog类,如下:

    //Dog
    @interface Dog : NSObject
    @end
    
    #import "Dog.h"
    
    @implementation Dog
    
    + (void)load {
        NSLog(@"====Dog");
    }
    
    @end
    ========================
    
    //Dog+Test
    @interface Dog (Test)
    @end
    
    @implementation Dog (Test)
    
    + (void)load {
        NSLog(@"====Dog+Test");
    }
    
    @end
    

    结论2:如果Cat在最前,则本类的调用顺序就是Animal、Cat、Dog;如果Dog在最前,则为Dog、Animal、Cat;如果Animal在最前,则Animal的打印也在最前,Dog和Cat的顺序则根据编译顺序排序。由此可见,有依赖关系的,则先调用父类的+load,没有依赖关系的,则按照编译顺序调用+load方法,分类的+load方法的调用顺序依然遵循编译顺序调用。


    三、分类不能直接添加属性
    static const char MZNameKey;
    static const void *MZAddressKey = &MZAddressKey;
    

    补充:

    Selector,Method,IMP 的关系:
    一个类(Class)持有一个分发表,在运行期分发消息,表中的每一个实体代表一个方法(Method),它的名字叫做选择子(SEL),对应着一种方法实现(IMP)。

    以下是Method的底层,实际上是一个method_t结构体。

    struct method_t {
        SEL name; //函数名
        const char *types; //编码(返回值类型、参数类型)
        IMP imp;//指向函数的指针(函数地址)
    };
    

    相关文章

      网友评论

          本文标题:iOS:Category浅析

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