动态添加方法可以在动态创建的类中添加也可以在已存在的类中添加,先来看下动态添加方法的定义函数:
OBJC_EXPORT BOOL
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
函数分析:
参数1:Class 要向其添加方法的类;
参数2:SEL 指定要添加的方法的名称;
参数3:IMP 一个新方法的实现函数,函数至少有两个参数id self
和SEL _cmd
;
参数4:types 描述方法参数类型的字符数组,参考;
返回值: 方法添加成功,则返回 YES,否则返回 NO(例如:该类已包含具有该名称的方法实现)。
我们以已经存在的类中添加方法为例,先创建一个Person类:
@interface Person : NSObject
@end
先为Person类添加一个实例对象方法(减号方法):
Person *p = Person.new;
BOOL isAdd = class_addMethod(Person.class, @selector(eat:), (IMP)EatFunction, "v@:@");
[p performSelector:@selector(eat:) withObject:@"芒果"];
// 带返回值写法
// class_addMethod(Person.class, @selector(eat:), (IMP)EatFunction, "@@:@");
// NSString *food = [p performSelector:@selector(eat:) withObject:@"芒果"];
v@:
表示一个void
类型的方法,无返回值,后面一个@
表示一个对象类型的参数(例中NSString类型);由于Person类中没有声明eat方法,无法直接用p对象调用,所以通过performSelector执行我们动态添加的eat方法。
void EatFunction( id self, SEL _cmd, NSString *food) {
NSLog(@"%@",food);
}
// 带返回值写法
//NSString * EatFunction( id self, SEL _cmd, NSString *food) {
// NSLog(@"%@",food);
// return food;
//}
先为Person类添加一个类对象方法(加号方法):
之前我们说过,类对象方法是存储在该类对应的元类中的,所以添加类方法需要向元类中添加,获取元类的有两中方法:
1、通过objc_getMetaClass()
函数,传入参数为C格式的类名称字符串,返回元类
OBJC_EXPORT Class _Nullable
objc_getMetaClass(const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
2、通过object_getClass()
函数,注意这个函数既可以获取该类,也可以获取该类对应的元类,就看你传入的参数类型;当传入参数为Perosn类的实例对象即p时,返回的是该类;当传入的参数为Perosn类对象时,返回的就是Person类的元类
OBJC_EXPORT Class _Nullable
object_getClass(id _Nullable obj)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
注:虽然object_getClass()
函数的参数是id
(任意对象)类型,但是类也是对象,所以可以把类对象当作参数。
添加方法时稍微有些变化:
class_addMethod(object_getClass(Person.class), @selector(eat:), (IMP)EatFunction, "v@:@");
// 或者
// BOOL isAdd = class_addMethod(objc_getMetaClass(NSStringFromClass(Person.class).UTF8String), @selector(eat:), (IMP)EatFunction, "v@:@");
执行方需要用Person
去调用
[Person performSelector:@selector(eat:) withObject:@"黄瓜"];
有人会疑问performSelector:
不是实例对象方法吗?为什么用类对象去调用也可以实现呢?还记得这张图吗?
元类中的superclass
指针指向其父类对应的的元类,而根类(NSObject
)的元类的superclass
指针却指向的是根类的实例类(上图中间一排,普通类),所以当Person
调用类方法时,如果Person类没有该方法,就会一级一级向父类的元类中找,如果找到根类的元类还是没有该类方法的实现,就向根类的普通类中去找相应的实例方法
的实现,如果找到了也可以调用,如果还是没有程序就会Crash。