1.RunTime方法交换
//self是类对象,testExchangeInstanceMethod是实例方法,查询实例方法需要在类方法中查询,即可直接在类对象中查询
Method instanceMethod = class_getInstanceMethod(self, @selector(testExchangeInstanceMethod));
Method instanceMethodAfter = class_getInstanceMethod(self, @selector(testExchangeInstanceMethodAfter));
method_exchangeImplementations(instanceMethod, instanceMethodAfter);
//self是类对象,testExchangeClassMethod是类方法,查询类方法需要在元类中查询,该方法多了层cls->getMeta()
Method classMethod = class_getClassMethod(self, @selector(testExchangeClassMethod));
Method classMethodAfter = class_getClassMethod(self, @selector(testExchangeClassMethodAfter));
method_exchangeImplementations(classMethod, classMethodAfter);
2.原理
2.1 class_getInstanceMethod
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
// Search method lists, try method resolver, etc.(搜索方法列表,尝试方法解析器)
lookUpImpOrNil(cls, sel, nil,
NO/*initialize*/, NO/*cache*/, YES/*resolver*/);
return _class_getMethod(cls, sel);
}
cls与sel都不能为空
lookUpImpOrNil 查找sel方法,并填充到缓存中
_class_getMethod源码
static Method _class_getMethod(Class cls, SEL sel)
{
mutex_locker_t lock(runtimeLock);
return getMethod_nolock(cls, sel);
}
static method_t *
getMethod_nolock(Class cls, SEL sel)
{
method_t *m = nil;
runtimeLock.assertLocked();
// fixme nil cls?
// fixme nil sel?
assert(cls->isRealized());
while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
cls = cls->superclass;
}
return m;
}
getMethodNoSuper_nolock查找的方法,如果当前cls没找到,沿着superclass指针查找。
getMethodNoSuper_nolock源码
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
assert(cls->isRealized());
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list(*mlists, sel);
if (m) return m;//找到即return
}
return nil;
}
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
return findMethodInSortedMethodList(sel, mlist);
} else {
// Linear search of unsorted method list(未排序方法列表的线性搜索)
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
return nil;
}
search_method_list,如果已经排序使用findMethodInSortedMethodList方法查找,这个方法二分查找;否则顺序查找,找到meth.name == sel即返回。
小结:class_getInstanceMethod从self中查找sel,并返回method_t。
我们都知道实例方法是存储在类方法中的,class_getInstanceMethod是在静态方法中调用的,即self是指当前类,符合实例方法是存储在类中的。
2.2 method_exchangeImplementations
void method_exchangeImplementations(Method m1, Method m2)
{
if (!m1 || !m2) return;//m1,m2都不能为空
mutex_locker_t lock(runtimeLock);
//本质交换了imp,name和types没变,不影响正常调用
IMP m1_imp = m1->imp;
m1->imp = m2->imp;
m2->imp = m1_imp;
..
}
//方法数据结构
struct method_t {
SEL name;//名称
const char *types;//类型:v16@0:8
MethodListIMP imp;//方法签名:如:(objc-test`-[Person addDynamicWalkInstance] at Person.m:36)
//通过名称排序
struct SortBySELAddress :
public std::binary_function<const method_t&,
const method_t&, bool>
{
bool operator() (const method_t& lhs,
const method_t& rhs)
{ return lhs.name < rhs.name; }
};
};
这个方法适应与m1和m2都不能为空,仅交换了m1与m2的imp指针,这也是为什么交换后调用原来的方法名是调用交换后的方法。
2.3 class_getClassMethod
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);//cls->getMeta()不是类对象,是元类
}
类的方法是存储在元类中的,class_getClassMethod是在静态方法调用的,cls指向的是当前类,通过cls->getMeta()找到元类。即交换类方法需要找到元类并交换。
类和实例方法的存储结构是相同的,只要找到对应的类和元类的cls,就能正确交换方法。
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
getMeta如果是元类,直接返回this;否则返回对应的isa指针。