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指針。