实例方法与类方法的归属

2021-07-13  本文已影响0人  杨奇

我们知道实例方法存储在类中,类方法存储在元类中
今天要探索的是,类方法是属于类还是属于元类
1.创建一个类,定义一个实例方法和类方法做比较

@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello{
    NSLog(@"LGPerson say : Hello!!!");
}

+ (void)sayHappy{
    NSLog(@"LGPerson say : Happy!!!");
}
@end

main.m中这样定义

void lgObjc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //获取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        
        LGLog(@"Method, name: %@", key);
    }
    free(methods);
}

void lgInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

void lgClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    // 元类 为什么有 sayHappy 类方法 0 1
    //
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

void lgIMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    // - (void)sayHello;
    // + (void)sayHappy;
    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));

    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        // 0x0000000100000000
        // LGTeacher *teacher = [LGTeacher alloc];

        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        lgObjc_copyMethodList(pClass);

        lgInstanceMethod_classToMetaclass(pClass);
        lgClassMethod_classToMetaclass(pClass);
        NSLog(@"Hello, World!");
    }
    return 0;
}

2.lgObjc_copyMethodList用于获取类的方法列表

3.lgInstanceMethod_classToMetaclass用于获取类的实例方法

4.lgClassMethod_classToMetaclass用于获取类的类方法

  1. lgIMP_classToMetaclass用户获取方法的调用
打印后结果如下
输出
这么看未免太乱,依次单个分析
lgObjc_copyMethodList 类的方法列表
Method, name: sayHello

在这个函数中获取的是LGPerson类的方法列表,因为实例方法储存在类中,类方法存储在元类中,所以只打印了sayHello方法。

lgInstanceMethod_classToMetaclass 获取类的实例方法
class_getInstanceMethod
看打印
lgInstanceMethod_classToMetaclass - 0x1000081e8--0x0--0x0--0x100008180
lgClassMethod_classToMetaclass 获取类的类方法
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}
-------------------------
// NOT identical to this->ISA when this is a metaclass
// 当这是一个元类时,返回。否则继续查找。
Class getMeta() {
    if (isMetaClassMaybeUnrealized()) return (Class)this;
    else return this->ISA()
}

图示如下:


获取类方法流程.jpg
lgClassMethod_classToMetaclass - 0x0--0x0--0x100008180--0x100008180

了解底层逻辑后,分析起来就得心应手了

lgIMP_classToMetaclass 返回方法的具体实现

底层代码

IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    lockdebug_assert_no_locks_locked_except({ &loadMethodLock });

    imp = lookUpImpOrNilTryCache(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    // Translate forwarding function to C-callable external version
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}

打印结果分析验证

gIMP_classToMetaclass - 0x100003d20--0x7fff2049c5c0--0x7fff2049c5c0--0x100003d50

总结

上一篇 下一篇

猜你喜欢

热点阅读