runtime版本objc4-723
class_addMethod
申明于 runtime.h:
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);
實現(xiàn)于objc-class-old.mm:
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
{
IMP old;
if (!cls) return NO;
old = _class_addMethod(cls, name, imp, types, NO);
return !old;
}
這里調(diào)用了同個文件的static IMP _class_addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
函數(shù)董朝。該函數(shù)的主要功能是:
- 判斷是否存在
name
對應的Method
:- 若存在欲虚,則返回該
Method
的實現(xiàn)曼振; - 若不存在栅受,則創(chuàng)建一個含有一個
Method
的old_method_list
勿侯,將其地址插入methodLists
所指數(shù)組,并返回nil
抬闯。
- 若存在欲虚,則返回該
該版本的
struct objc_class
中的methodLists
成員是old_method_list **
類型浑塞,而非objc_method_list **
。申明于objc-runtime-old.h孕惜。
具體實現(xiàn)如下:
static IMP _class_addMethod(Class cls, SEL name, IMP imp,
const char *types, bool replace)
{
old_method *m;
IMP result = nil;
if (!types) types = "";
mutex_locker_t lock(methodListLock);
if ((m = _findMethodInClass(cls, name))) {
// already exists
// fixme atomic
result = method_getImplementation((Method)m);
if (replace) {
method_setImplementation((Method)m, imp);
}
} else {
// fixme could be faster
old_method_list *mlist =
(old_method_list *)calloc(sizeof(old_method_list), 1);
mlist->obsolete = fixed_up_method_list;
mlist->method_count = 1;
mlist->method_list[0].method_name = name;
mlist->method_list[0].method_types = strdup(types);
mlist->method_list[0].method_imp = imp;
_objc_insertMethods(cls, mlist, nil);
if (!(cls->info & CLS_CONSTRUCTING)) {
flush_caches(cls, NO);
} else {
// in-construction class has no subclasses
flush_cache(cls);
}
result = nil;
}
return result;
}
其中void _objc_insertMethods(Class cls, old_method_list *mlist, old_category *cat)
實現(xiàn)在 objc-runtime-old.mm中愧薛。該函數(shù)的主要功能是:
- 若
cls
的methodLists
為空,則將mlist
賦予methodLists
衫画,函數(shù)返回毫炉; - 調(diào)用
_objcTweakMethodListPointerForClass(cls)
,確保methodLists
指向一個old_method_list
數(shù)組削罩; - 若
methodLists
所指數(shù)組已滿瞄勾,則將methodLists
指向新的一個長度+1的數(shù)組费奸; - 將
methodLists
所指數(shù)組的所有元素右移一個位; - 將
mlist
賦予methodLists
所指數(shù)組第一個元素进陡。
具體實現(xiàn)如下:
void _objc_insertMethods(Class cls, old_method_list *mlist, old_category *cat)
{
old_method_list ***list;
old_method_list **ptr;
ptrdiff_t endIndex;
size_t oldSize;
size_t newSize;
if (!cls->methodLists) {
// cls has no methods - simply use this method list
cls->methodLists = (old_method_list **)mlist;
cls->setInfo(CLS_NO_METHOD_ARRAY);
return;
}
// Log any existing methods being replaced
if (PrintReplacedMethods) {
int i;
for (i = 0; i < mlist->method_count; i++) {
extern IMP findIMPInClass(Class cls, SEL sel);
SEL sel = sel_registerName((char *)mlist->method_list[i].method_name);
IMP newImp = mlist->method_list[i].method_imp;
IMP oldImp;
if ((oldImp = findIMPInClass(cls, sel))) {
logReplacedMethod(cls->name, sel, ISMETA(cls),
cat ? cat->category_name : nil,
oldImp, newImp);
}
}
}
// Create method list array if necessary
_objcTweakMethodListPointerForClass(cls);
list = &cls->methodLists;
// Locate unused entry for insertion point
ptr = *list;
while ((*ptr != 0) && (*ptr != END_OF_METHODS_LIST))
ptr += 1;
// If array is full, add to it
if (*ptr == END_OF_METHODS_LIST)
{
// Calculate old and new dimensions
endIndex = ptr - *list;
oldSize = (endIndex + 1) * sizeof(void *);
newSize = oldSize + sizeof(old_method_list *); // only increase by 1
// Grow the method list array by one.
*list = (old_method_list **)realloc(*list, newSize);
// Zero out addition part of new array
bzero (&((*list)[endIndex]), newSize - oldSize);
// Place new end marker
(*list)[(newSize/sizeof(void *)) - 1] = END_OF_METHODS_LIST;
// Insertion point corresponds to old array end
ptr = &((*list)[endIndex]);
}
// Right shift existing entries by one
bcopy (*list, (*list) + 1, (uint8_t *)ptr - (uint8_t *)*list);
// Insert at method list at beginning of array
**list = mlist;
}
static void _objcTweakMethodListPointerForClass(Class cls)
的主要功能是:
- 若
cls->methodLists
有值愿阐,且cls->info
不含有CLS_NO_METHOD_ARRAY
,則函數(shù)返回趾疚; - 創(chuàng)建一個長度為4的數(shù)組缨历;
- 將原來的
cls->methodLists
賦予數(shù)組第一個元素; - 將
END_OF_METHODS_LIST
賦予數(shù)組最后一個元素糙麦; - 將數(shù)組的地址賦予
cls->methodLists
辛孵; - 取消
cls->info
的CLS_NO_METHOD_ARRAY
標記。
具體實現(xiàn)如下:
static void _objcTweakMethodListPointerForClass(Class cls)
{
old_method_list * originalList;
const int initialEntries = 4;
size_t mallocSize;
old_method_list ** ptr;
// Do nothing if methodLists is already an array.
if (cls->methodLists && !(cls->info & CLS_NO_METHOD_ARRAY)) return;
// Remember existing list
originalList = (old_method_list *) cls->methodLists;
// Allocate and zero a method list array
mallocSize = sizeof(old_method_list *) * initialEntries;
ptr = (old_method_list **) calloc(1, mallocSize);
// Insert the existing list into the array
ptr[initialEntries - 1] = END_OF_METHODS_LIST;
ptr[0] = originalList;
// Replace existing list with array
cls->methodLists = ptr;
cls->clearInfo(CLS_NO_METHOD_ARRAY);
}
Class的methodLists可能是:
- 空指針喳资,此時Class沒有方法七问,(info & CLS_NO_METHOD_ARRAY) == true沪饺。
- objc_method結(jié)構(gòu)體指針,此時Class有一個method_list。
- objc_method結(jié)構(gòu)體指針數(shù)組上祈,此時Class有多個method_list。