class_addMethod

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);

实现于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;
}

这里调用了同个文件的static IMP _class_addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)函数。该函数的主要功能是:

  • 判断是否存在name对应的Method
    1. 若存在,则返回该Method的实现;
    2. 若不存在,则创建一个含有一个Methodold_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中。该函数的主要功能是:

  1. clsmethodLists为空,则将mlist赋予methodLists,函数返回;
  2. 调用_objcTweakMethodListPointerForClass(cls),确保methodLists指向一个old_method_list数组;
  3. methodLists所指数组已满,则将methodLists指向新的一个长度+1的数组;
  4. methodLists所指数组的所有元素右移一个位;
  5. 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)的主要功能是:

  1. cls->methodLists有值,且cls->info不含有CLS_NO_METHOD_ARRAY,则函数返回;
  2. 创建一个长度为4的数组;
  3. 将原来的cls->methodLists赋予数组第一个元素;
  4. END_OF_METHODS_LIST赋予数组最后一个元素;
  5. 将数组的地址赋予cls->methodLists
  6. 取消cls->infoCLS_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可能是:

  1. 空指针,此时Class没有方法,(info & CLS_NO_METHOD_ARRAY) == true。
  2. objc_method结构体指针,此时Class有一个method_list。
  3. objc_method结构体指针数组,此时Class有多个method_list。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,686评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,182评论 0 7
  • //联系人:石虎QQ: 1224614774昵称:嗡嘛呢叭咪哄 objc_class结构体 一、类在OC中是obj...
    石虎132阅读 2,179评论 0 21
  • Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的...
    有一种再见叫青春阅读 579评论 0 3
  • 原文出处:南峰子的技术博客 Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了...
    _烩面_阅读 1,220评论 1 5