1.Runtime交换
Class class = object_getClass((id)self);//元类
SEL originalSelector = @selector(testExchangeClassMethod2);
SEL swizzledSelector = @selector(testExchangeClassMethodAfter2);
Method originalMethod = class_getClassMethod(self, originalSelector);
Method swizzledMethod = class_getClassMethod(self, swizzledSelector);
//class_addMethod:查询originalSelector是否存在
//存在为NO;不存在为YES,并且会当前类动态添加originalSelector
//后两个参数没有用到
BOOL isAddOriginalMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
//class_addMethod为类添加了未实现的方法,但是originalMethod仍然是空
//重新获取就有值
Method originalMethod1 = class_getClassMethod(self, originalSelector);
if (isAddOriginalMethod) {
//动态添加originalMethod,并把imp指针赋值给swizzledSelector达到交换
//直接替换也没有毛病
//相当于调用了class_addMethod方法,会检测swizzledSelector是否存在
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
//如果originalMethod未实现,可以重新获取originalMethod1
method_exchangeImplementations(originalMethod1, swizzledMethod);//有一个方法为空,不会交换
}
问题:method_exchangeImplementations与class_replaceMethod交换有什么区别?
method_exchangeImplementations原理
2.原理
2.1class_getClassMethod
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
//getMeta对isa查找的封装,如果是元类返回self,如果是类则返回isa。与self.class查找有点像
return class_getInstanceMethod(cls->getMeta(), sel);//cls->getMeta()不是类对象,是元类
}
由于cls是类,方法存储在元类中,需要通过isa找到元类中中的sel。
2.2class_addMethod
BOOL
class_addMethod(Class cls, SEL name, IMP imp, const char *types)
{
if (!cls) return NO;
mutex_locker_t lock(runtimeLock);
return ! addMethod(cls, name, imp, types ?: "", NO);
}
static IMP
addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
{
IMP result = nil;
...
method_t *m;
if ((m = getMethodNoSuper_nolock(cls, name))) {//SEL name已经实现
// already exists
if (!replace) {
result = m->imp;//name方法存在,返回对应的imp
} else {
result = _method_setImplementation(cls, m, imp);//把m.imp=imp
}
} else {//SEL name没有实现,imp和name赋给newlist->first
// fixme optimize
method_list_t *newlist;
newlist = (method_list_t *)calloc(sizeof(*newlist), 1);
newlist->entsizeAndFlags =
(uint32_t)sizeof(method_t) | fixed_up_method_list;
newlist->count = 1;
newlist->first.name = name;
newlist->first.types = strdupIfMutable(types);
newlist->first.imp = imp;
prepareMethodLists(cls, &newlist, 1, NO, NO);//修正排序
cls->data()->methods.attachLists(&newlist, 1);//添加到data()->methods中
flushCaches(cls);//添加完成填充到缓存
result = nil;
}
return result;
}
- getMethodNoSuper_nolock(cls, name),查找cls中的方法name,如果找到返回m.
- 如果m没有,即cls中不存在name方法
分配一个method_list_t结构体,并赋值name,imp,type第一个;
修正方法排序;
添加到cls->data()->methods中;
填充到缓存;
即生成一个新的newlist,并追加到原有的方法列表中。 - 如果m不为空
如果replace为NO,即不交换方法,不做处理;如果replace为yes,把m.imp=imp.
没有就新生成一个,追加到原来的方法列表中,如果有且replace为yes,把imp给name对应的方法。
isAddOriginalMethod如果为NO,交换的方法肯定存在,用method_exchangeImplementations即可;如果为yes,即新添加了方法,走class_replaceMethod方法。
2.3class_replaceMethod
IMP
class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
{
if (!cls) return nil;
mutex_locker_t lock(runtimeLock);
return addMethod(cls, name, imp, types ?: "", YES);
}
本质也是调用了addMethod方法。
小结:
1.如果两个方法都明确确定存在,使用method_exchangeImplementations即可
2.如果原方法不确定是否存在,可用class_addMethod判断
3.如果交换后的方法不确定是否存在,可用class_replaceMethod
4.如果原方法与交换后的方法都不确定是否存在,可用class_addMethod与
class_replaceMethod结合,不过这种应用场景不明确。
原理这样,但什么方法都不存在,还交换什么,大部分直接使用method_exchangeImplementations即可;如果遇到看不到源码,不明确的,可以用class_replaceMethod。
如果方法不存在,class_addMethod之后,再Method originalMethod1 = class_getClassMethod(self, originalSelector);获取之后,originalMethod1就存在了,再使用method_exchangeImplementations交换也是可以的。