美文网首页
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