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);
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;
}
这里调用了同个文件的static IMP _class_addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
函数。该函数的主要功能是:
- 判断是否存在
name
对应的Method
:- 若存在,则返回该
Method
的实现; - 若不存在,则创建一个含有一个
Method
的old_method_list
,将其地址插入methodLists
所指数组,并返回nil
。
- 若存在,则返回该
该版本的
struct objc_class
中的methodLists
成员是old_method_list **
类型,而非objc_method_list **
。申明于objc-runtime-old.h。
具体实现如下:
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)
实现在 objc-runtime-old.mm中。该函数的主要功能是:
- 若
cls
的methodLists
为空,则将mlist
赋予methodLists
,函数返回; - 调用
_objcTweakMethodListPointerForClass(cls)
,确保methodLists
指向一个old_method_list
数组; - 若
methodLists
所指数组已满,则将methodLists
指向新的一个长度+1的数组; - 将
methodLists
所指数组的所有元素右移一个位; - 将
mlist
赋予methodLists
所指数组第一个元素。
具体实现如下:
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
,则函数返回; - 创建一个长度为4的数组;
- 将原来的
cls->methodLists
赋予数组第一个元素; - 将
END_OF_METHODS_LIST
赋予数组最后一个元素; - 将数组的地址赋予
cls->methodLists
; - 取消
cls->info
的CLS_NO_METHOD_ARRAY
标记。
具体实现如下:
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结构体指针,此时Class有一个method_list。
- objc_method结构体指针数组,此时Class有多个method_list。