美文网首页
iOS - Runtime-动态添加方法和属性

iOS - Runtime-动态添加方法和属性

作者: CDLOG | 来源:发表于2018-08-24 17:57 被阅读92次

    动态添加方法

    如果一个类方法非常多,其中可能许多方法暂时用不到。而加载类方法到内存的时候需要给每个方法生成映射表,又比较耗费资源。此时可以使用RunTime动态添加方法

    动态给某个类添加方法,相当于懒加载机制,类中许多方法暂时用不到,那么就先不加载,等用到的时候再去加载方法。

    动态添加方法的方法:
    首先我们先不实现对象方法,对象发送performSelector: 消息去动态加载方法。

    需要头文件

    import <objc/message.h>

    People *p = [[People alloc]init];
    // 当调用 P中没有实现的方法时,动态加载方法
    //调用People的eat方法
    [p performSelector:@selector(eat)];
    

    当调用了没有实现的对象方法的时,就会调用+(BOOL)resolveInstanceMethod:(SEL)sel方法。
    当调用了没有实现的类方法的时候,就会调用+(BOOL)resolveClassMethod:(SEL)sel方法。

    +(BOOL)resolveInstanceMethod:(SEL)sel
    {
        // 动态添加eat方法,有参数的话加:
        // 首先判断sel是不是eat方法 也可以转化成字符串进行比较。    
        if (sel == @selector(eat)) {
        /** 
         第一个参数: cls:给哪个类添加方法
         第二个参数: SEL name:添加方法的编号
         第三个参数: IMP imp: 方法的实现,函数入口,函数名可与方法名不同(建议与方法名相同)
         第四个参数: types :方法类型(函数的返回值类型,参数类型),需要用特定符号,参考API
    v -> void 表示无返回值
    @ -> object 表示id参数
    : -> method selector 表示SEL
         */
          class_addMethod(self, sel, (IMP)eat , "v@:");
            // 处理完返回YES
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    

    class_addMethod给类创建方法的详细参数解释

    cls : 表示给哪个类添加方法,这里要给Person类添加方法,self即代表Person。
    SEL name : 表示添加方法的编号。因为这里只有一个方法需要动态添加,并且之前通过判断确定sel就是eat方法,所以这里可以使用sel。
    IMP imp : 表示方法的实现(直接在.m写函数),函数入口,函数名可与方法名不同(建议与方法名相同)需要自己来实现这个函数。每一个方法都默认带有两个隐式参数
    self : 方法调用者 _cmd : 调用方法的标号,可以写也可以不写。

    void eat(id self ,SEL _cmd)
    {
          // 实现内容
          NSLog(@"%@的%@方法动态实现了",self,NSStringFromSelector(_cmd));
    }
    

    types : 表示方法类型,需要用特定符号。系统提供的例子中使用的是"v@:",我们来到API中看看"v@:"指定的方法是什么类型的。

    type表:

    image.png

    动态添加有参数的方法

    如果是有参数的方法,需要对方法的实现和class_addMethod方法内方法类型参数做一些修改。
    方法实现:因为在C语言函数中,所以对象参数类型只能用id代替。
    方法类型参数:因为添加了一个id参数,所以方法类型应该为"v@:@"
    来看一下代码

    +(BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == @selector(eat:)) {
            class_addMethod(self, sel, (IMP)aaaa , "v@:@");
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    void aaaa(id self ,SEL _cmd,id Num)
    {
        // 实现内容
        NSLog(@"%@的%@方法动态实现了,参数为%@",self,NSStringFromSelector(_cmd),Num);
    }
    
    

    调用eat:函数

    Person *p = [[Person alloc]init];
    [p performSelector:@selector(eat:)withObject:@"log"]
    

    动态添加属性

    属性其实是对象的属性和内存的某个对象产生关联(属性指向内存的对象)。
    动态添加属性其实就是动态的给属性添加关联。
    动态添加属性只能用分类。

    动态添加属性例子

    这里给NSObject添加name属性,创建NSObject的分类
    我们可以使用@property给分类添加属性

    @property(nonatomic,strong)NSString *name;
    

    虽然在分类中可以写@property
    添加属性,只会生成set,get的声明,但是不会自动生成私有属性,需要自己添加属性关联以及实现set,get方法。

    RunTime提供了动态添加属性和获得属性的方法。

    //在set实现添加属性关联
    -(void)setName:(NSString *)name
    {
    
        objc_setAssociatedObject(self, @"name",name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    -(NSString *)name
    {
    //在get获取属性关联
        return objc_getAssociatedObject(self, @"name");    
    }
    

    1,动态添加属性关联

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

    参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self。
    参数二:void * == id key : 属性名,根据key获取关联对象的属性的值,在objc_getAssociatedObject中通过次key获得属性的值并返回。
    参数三:id value : 关联的值,也就是set方法传入的值给属性去保存。
    参数四:objc_AssociationPolicy policy : 策略,属性以什么形式保存。
    objc_AssociationPolicy policy有以下几种

    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0,  // 指定一个弱引用相关联的对象
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 指定相关对象的强引用,非原子性
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  // 指定相关的对象被复制,非原子性
        OBJC_ASSOCIATION_RETAIN = 01401,  // 指定相关对象的强引用,原子性
        OBJC_ASSOCIATION_COPY = 01403     // 指定相关的对象被复制,原子性   
    };
    

    2,动态获得属性关联的值

    objc_getAssociatedObject(id object, const void *key);
    
    

    参数一:id object : 获取哪个对象里面的关联的属性。
    参数二:void * == id key : 什么属性,与objc_setAssociatedObject中的key相对应,即通过key值取出value。

    调用:
    此时已经成功给NSObject添加name属性,并且NSObject对象可以通过点语法为属性赋值。

    NSObject *objc = [[NSObject alloc]init];
    objc.name = @"log";
    NSLog(@"%@",objc.name);
    

    相关文章

      网友评论

          本文标题:iOS - Runtime-动态添加方法和属性

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