class_addMethod 給一個類添加方法
正常添加一個繼承鏈中不存在的方法, 結(jié)果能夠正常使用
如果這個類本身就有該方法的申明和實現(xiàn)則添加失敗
如果這個類本身僅僅有該方法的申明那么是可以添加成功的
如果這個類本身僅僅有該方法的實現(xiàn)則會添加失敗(預(yù)料之中,因為所謂申明僅僅是編譯器的事,編譯完也就沒有.h的事了)
如果這個類沒有該方法,但是父類有呢?結(jié)果是父類不會影響到子類添加方法的結(jié)果, 依然可以添加成功
源碼分析
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;
// 加鎖
runtimeLock.assertLocked();
// 檢測該cls是否存在
checkIsKnownClass(cls);
// 方法簽名不可為空
assert(types);
// 類必須是初始化后的
assert(cls->isRealized());
// 根據(jù) 方法名去cls這個類(不沿著繼承鏈向上查詢)查詢有沒有對應(yīng)的方法實現(xiàn)
method_t *m;
if ((m = getMethodNoSuper_nolock(cls, name))) {
// 如果有的話, 因為replace傳的是NO, 也不會替換原來實現(xiàn)
if (!replace) {
result = m->imp;
} else {
result = _method_setImplementation(cls, m, imp);
}
} else {
// 沒有則生成一個新的方法并放在cls的方法列表最后
method_list_t *newlist;
// 1.開辟空間
newlist = (method_list_t *)calloc(sizeof(*newlist), 1);
// 2.初始化
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;
// 3.準備添加
prepareMethodLists(cls, &newlist, 1, NO, NO);
// 4.添加
cls->data()->methods.attachLists(&newlist, 1);
flushCaches(cls);
result = nil;
}
return result;
}
method_setImplementation 修改一個方法的實現(xiàn)部分
IMP method_setImplementation(Method m, IMP imp)
{
// 可以看出來修改一個方法的實現(xiàn), 是針對Method這個結(jié)構(gòu)體本身的行為
mutex_locker_t lock(runtimeLock);
return _method_setImplementation(Nil, m, imp);
}
static IMP _method_setImplementation(Class cls, method_t *m, IMP imp)
{
// 加鎖
runtimeLock.assertLocked();
// 判空
if (!m) return nil;
if (!imp) return nil;
// 直接method_t這個結(jié)構(gòu)體的imp成員指向了新的imp
IMP old = m->imp;
m->imp = imp;
flushCaches(cls);
updateCustomRR_AWZ(cls, m);
return old;
}
method_exchangeImplementations 交換兩個方法的實現(xiàn)
void method_exchangeImplementations(Method m1, Method m2)
{
// 判空
if (!m1 || !m2) return;
// 加鎖
mutex_locker_t lock(runtimeLock);
// 直接Method這個結(jié)構(gòu)體的imp成員指向進行了交換就完事了
IMP m1_imp = m1->imp;
m1->imp = m2->imp;
m2->imp = m1_imp;
flushCaches(nil);
updateCustomRR_AWZ(nil, m1);
updateCustomRR_AWZ(nil, m2);
}
method_getImplementation 直接返回了Method這個結(jié)構(gòu)體的imp
IMP method_getImplementation(Method m)
{
return m ? m->imp : nil;
}
class_getMethodImplementation 獲取一個類的對應(yīng)方法名的IMP
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(cls, sel, nil,
YES/*initialize*/, YES/*cache*/, YES/*resolver*/);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
if (imp == _objc_msgForward_impcache) return nil;
else return imp;
}
// 執(zhí)行查找imp和轉(zhuǎn)發(fā)的代碼
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// 如果cache是YES阶女,則從緩存中查找IMP闲坎。如果是從cache3函數(shù)進來,則不會執(zhí)行cache_getImp()函數(shù)
if (cache) {
// 通過cache_getImp函數(shù)查找IMP,查找到則返回IMP并結(jié)束調(diào)用
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
runtimeLock.read();
// 判斷類是否已經(jīng)被創(chuàng)建,如果沒有被創(chuàng)建埋泵,則將類實例化
if (!cls->isRealized()) {
// 對類進行實例化操作
realizeClass(cls);
}
// 第一次調(diào)用當前類的話抑党,執(zhí)行initialize的代碼
if (initialize && !cls->isInitialized()) {
// 對類進行初始化,并開辟內(nèi)存空間
_class_initialize (_class_getNonMetaClass(cls, inst));
}
retry:
runtimeLock.assertReading();
// 嘗試獲取這個類的緩存
imp = cache_getImp(cls, sel);
if (imp) goto done;
{
// 如果沒有從cache中查找到拧咳,則從方法列表中獲取Method
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
// 如果獲取到對應(yīng)的Method,則加入緩存并從Method獲取IMP
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
{
unsigned attempts = unreasonableClassCount();
// 循環(huán)獲取這個類的緩存IMP 或 方法列表的IMP
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// 獲取父類緩存的IMP
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// 如果發(fā)現(xiàn)父類的方法囚灼,并且不再緩存中骆膝,在下面的函數(shù)中緩存方法
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
break;
}
}
// 在父類的方法列表中,獲取method_t對象灶体。如果找到則緩存查找到的IMP
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
// 如果沒有找到阅签,則嘗試動態(tài)方法解析
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst);
runtimeLock.read();
triedResolver = YES;
goto retry;
}
// 如果沒有IMP被發(fā)現(xiàn),并且動態(tài)方法解析也沒有處理蝎抽,則進入消息轉(zhuǎn)發(fā)階段
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlockRead();
return imp;
}
class_replaceMethod 將一個類的原有方法用新方法進行替換,如果原有方法為空,則直接調(diào)用class_addMethod將新方法添加上
IMP _Nullable 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);
}
static IMP addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
{
IMP result = nil;
// 加鎖
runtimeLock.assertLocked();
// 檢測該cls是否存在
checkIsKnownClass(cls);
// 方法簽名不可為空
assert(types);
// 類必須是初始化后的
assert(cls->isRealized());
// 根據(jù) 方法名去cls這個類(不沿著繼承鏈向上查詢)查詢有沒有對應(yīng)的方法實現(xiàn)
method_t *m;
if ((m = getMethodNoSuper_nolock(cls, name))) {
// 如果有的話, 因為replace傳的是YES, 會替換原來實現(xiàn)
if (!replace) {
result = m->imp;
} else {
result = _method_setImplementation(cls, m, imp);
}
} else {
// 沒有則生成一個新的方法并放在cls的方法列表最后
method_list_t *newlist;
// 1.開辟空間
newlist = (method_list_t *)calloc(sizeof(*newlist), 1);
// 2.初始化
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;
// 3.準備添加
prepareMethodLists(cls, &newlist, 1, NO, NO);
// 4.添加
cls->data()->methods.attachLists(&newlist, 1);
flushCaches(cls);
result = nil;
}
return result;
}