动态添加方法
如果一个类方法非常多,其中可能许多方法暂时用不到。而加载类方法到内存的时候需要给每个方法生成映射表,又比较耗费资源。此时可以使用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表:
动态添加有参数的方法
如果是有参数的方法,需要对方法的实现和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);
网友评论