美文网首页
class_replaceMethod详解

class_replaceMethod详解

作者: lltree | 来源:发表于2019-05-15 11:42 被阅读0次

    原文地址,此处只为学习

    class_replaceMethod

    作用:存在要替换的方法则替换原方法的IMP并返回原方法的IMP,,不存在原有方法则动态添加该方法并且返回nil(原方法未实现,可认为IMP未nil)

    IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
    {
        if (!cls) return nil;
    
        rwlock_writer_t lock(runtimeLock);
        return addMethod(cls, name, imp, types ?: "", YES);
    }
    

    从源码中可以看出,class_replaceMethod方法内部是调用了addMethod

    static IMP 
    addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
    {
        IMP result = nil;
    
        runtimeLock.assertWriting();
    
        assert(types);
        assert(cls->isRealized());
    
        method_t *m;
        if ((m = getMethodNoSuper_nolock(cls, name))) {
            // already exists
            if (!replace) {
                result = m->imp;
            } else {
                result = _method_setImplementation(cls, m, imp);
            }
        } else {
            // fixme optimize
            method_list_t *newlist;
            newlist = (method_list_t *)calloc(sizeof(*newlist), 1);
            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;
    
            prepareMethodLists(cls, &newlist, 1, NO, NO);
            cls->data()->methods.attachLists(&newlist, 1);
            flushCaches(cls);
    
            result = nil;
        }
    
        return result;
    }
    

    首先通过调用getMethodNoSuper_nolock去类的方法列表中查找,
    找到就判断传进来的replace

    如果为NO,就直接返回在方法列表中查找到的IMP,
    如果为YES,就交换IMP,并返回原来方法的IMP

    _method_setImplementation交换方法实现,返回原来的方法实现

    static IMP 
    _method_setImplementation(Class cls, method_t *m, IMP imp)
    {
        runtimeLock.assertWriting();
        if (!m) return nil;
        if (!imp) return nil;
        IMP old = m->imp;
        m->imp = imp;
        flushCaches(cls);
        updateCustomRR_AWZ(cls, m);
        return old;
    }
    

    如果没找到
    如果在方法列表中没有找到对应的方法实现,创建一个method_list_t,把方法信息赋值到method_list_t中.最后再和原来的方法列表进行合并,这种方式添加的方法返回IMP为nil

    接下来看一下实践吧

    @interface Person : NSObject
    - (void)run;
    
    @end
    @implementation Person
    
    @end
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        Person *person = [[Person alloc]init];
        self.person = person;
        Method methodNew = class_getInstanceMethod([self class], @selector(customImp));
        IMP methodIMPNew = method_getImplementation(methodNew);
        const char *typeEncoding = method_getTypeEncoding(methodNew);
        IMP replaceImp = class_replaceMethod([person class], @selector(run), methodIMPNew, typeEncoding);
    
        NSLog(@"methodIMPNew =%p,replaceImp = %p",methodIMPNew,replaceImp);
    }
    

    打印结果:methodIMPNew =0x106704440,replaceImp = 0x0
    Person类中的run方法并没有实现,所以在方法列表中是找不到的,在addMethod中就会创建一个集合,把methodIMPNew传进去,方法名还是原来的方法名,返回nil,所以上面打印的replaceImp = 0x0
    再看一下另一种情况

    @implementation Person
    - (void)run
    {
        NSLog(@"%s",__func__);
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        Person *person = [[Person alloc]init];
        self.person = person;
    
        Method methodOld = class_getInstanceMethod([self.person class], @selector(run));
        IMP methodIMPOld = method_getImplementation(methodOld);
        Method methodNew = class_getInstanceMethod([self class], @selector(customImp));
        IMP methodIMPNew = method_getImplementation(methodNew);
        const char *typeEncoding = method_getTypeEncoding(methodNew);
        //    //把run方法的IMP替换成eat
        IMP replaceImp = class_replaceMethod([self.person class], @selector(run), methodIMPNew, typeEncoding);
      
        NSLog(@"methodIMPOld = %p,methodIMPNew =%p,replaceImp = %p",methodIMPOld,methodIMPNew,replaceImp);
    }
    @end
    

    控制台打印:methodIMPOld = 0x105e8f640,methodIMPNew =0x105e8f3f0,replaceImp = 0x105e8f640
    class_replaceMethod返回的就是原来方法的IMP,但是此时run方法的IMP却指向了customImp方法的实现

    相关文章

      网友评论

          本文标题:class_replaceMethod详解

          本文链接:https://www.haomeiwen.com/subject/cqxpaqtx.html