OC底层原理08-方法、属性、成员变量的归属

一、准备工作

  • objc4可编译源码,可直接跳到文章最后,下载调试好的源码
  • 在源码中创建类GomuPerson
GomuPerson.h
@interface GomuPerson : NSObject
{
    //: 成员变量
    NSString *hobby;
    //: 特殊的成员变量,实例变量,能够被实例的对象的成员变量叫实例变量
    NSObject *obj;
}
//: 创建属性
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *sex;

//: 创建实例方法、类方法
- (void)sayNO;
+ (void)sayLove;

GomuPerson.m
//: 实现方法
- (void)sayNO{}
+ (void)sayLove{}

@end

二、通过runtime的api拿方法、属性、成员变量

2.1 通过gomu_copyMethodListGomuPerson里面的方法

//: 打印类中的所有方法
void gomu_copyMethodList(Class class){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(class, &count);
    for (unsigned int i = 0; i < count; i++) {
        Method const method = methods[i];
        //: 获取方法名
        NSString *methodName = NSStringFromSelector(method_getName(method));
        GOMULog(@"Method, name: %@",methodName);
    }
    free(methods);
}

//: 调用方法
GomuPerson *p = [GomuPerson alloc];
//: 获取实例方法
gomu_copyMethodList(object_getClass(p));
//: 打印
Method, name: sayNO
Method, name: sex
Method, name: setSex:
Method, name: .cxx_destruct
Method, name: name
Method, name: setName:

//: 获取类方法
gomu_copyMethodList(object_getClass([GomuPerson class]));
//: 打印
Method, name: sayLove

结论:

  • 对象方法存在
  • 系统在编译中会自动生成属性的getset方法
  • 系统在编译中也会生成c++.cxx_destruct方法
  • 类方法存在元类

2.2 通过gomu_copyPropertyList拿GomuPerson里面的属性

#pragma mark -- 获取属性
void gomu_copyPropertyList(Class class){
    unsigned int count = 0;
    objc_property_t *propertys = class_copyPropertyList(class, &count);
    for (unsigned int i = 0; i < count; i++) {
        objc_property_t property = propertys[i];
        //: 获取属性
        const char *propertyName = property_getName(property);
        GOMULog(@"Property, name: %s",propertyName);
    }
    free(propertys);
}

//: 调用方法
GomuPerson *p = [GomuPerson alloc];
//: 获取属性
gomu_copyPropertyList(object_getClass(p));
//: 打印
Property, name: name
Property, name: sex

结论:

  • 属性存在PropertyList
  • 成员变量不存在PropertyList

2.3 通过gomu_copyIvarList拿GomuPerson里面的成员变量

#pragma mark -- 获取成员变量
void gomu_copyIvarList(Class class){
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(class, &count);
    for (unsigned int i = 0; i < count; i++) {
        Ivar const ivar = ivars[i];
        //: 获取成员变量
        const char *ivarName = ivar_getName(ivar);
        GOMULog(@"Ivar, name: %s",ivarName);
    }
    free(ivars);
}
//: 调用方法
GomuPerson *p = [GomuPerson alloc];
//: 获取属性
gomu_copyIvarList(object_getClass(p));
//: 打印
Ivar, name: hobby
Ivar, name: obj
Ivar, name: _name
Ivar, name: _sex

结论

  • 成员变量存在IvarList
  • 系统编译中会自动给属性生成带_成员变量

2.4 通过class_getInstanceMethod判断对象方法和类方法的归属

void gomuInstanceMethod_classToMetaclass(Class pClass){
    //: 获取元类
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    //: 判断类中是否有对象方法`sayNO`:有
    Method method1 = class_getInstanceMethod(pClass, @selector(sayNO));
    //: 判断元类中是否有对象方法`sayNO`:没有
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayNO));
        //: 判断类中是否有类方法`sayLove`的对象方法:没有
    Method method3 = class_getInstanceMethod(pClass, @selector(sayLove));
    //: 判断类元中是否有类方法`sayLove`的对象方法:有
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayLove));
    
    GOMULog(@"%s : %p - %p - %p - %p",__func__,method1,method2,method3,method4);
    
    GOMULog(@"%s : %p - %p - %p - %p",__func__,method1,method2,method3,method4);

//: 调用
gomuInstanceMethod_classToMetaclass(pClass);

//: 打印
gomuInstanceMethod_classToMetaclass : 0x1000031e0 - 0x0 - 0x0 - 0x100003178
}

结论:

  • 对象方法存在
  • 类方法存在元类,并且以对象方法的形式存在元类

2.5 通过class_getClassMethod判断对象方法和类方法的归属

void gomuClassMethod_classToMetaclass(Class pClass){
    
    //: 获取元类
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    //: 判断类中是否有对象方法`sayNO`的类方法:没有
    Method method1 = class_getClassMethod(pClass, @selector(sayNO));
    //: 判断元类中是否有对象方法`sayNO`的类方法:没有
    Method method2 = class_getClassMethod(metaClass, @selector(sayNO));
    //: 判断类中是否有类方法`sayLove`:有
    Method method3 = class_getClassMethod(pClass, @selector(sayLove));
    //: 判断元类中是否有类方法`sayLove`:有
    Method method4 = class_getClassMethod(metaClass, @selector(sayLove));
    
    GOMULog(@"%s : %p - %p - %p - %p",__func__,method1,method2,method3,method4);

//: 调用
gomuClassMethod_classToMetaclass(pClass);

//: 打印
gomuClassMethod_classToMetaclass : 0x0 - 0x0 - 0x100003180 - 0x100003180
}

结论:

  • 不管还是元类中,都不会有对象方法类方法
  • 元类中,都有类方法
2.5.1 元类中为什么会有类方法,不是说类方法在元类是以对象方法的形成存在的吗,进入class_getClassMethod内部查看源码
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;
    //: 当cls是元类,走到这里,getMeta()返回元类
    //: 所以 class_getClassMethod 传入元类相当于 class_getInstanceMethod传入元类
    return class_getInstanceMethod(cls->getMeta(), sel);
}

//: 如果是元类,则直接返回自己
Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}

结论:

  • class_getClassMethodcls传入的是元类,则会调用class_getInstanceMethod,执行到cls->getMeta()返回自己。
  • class_getClassMethod(metaClass, @selector(sayLove))等价于class_getInstanceMethod(metaClass, @selector(sayLove));
  • 类方法元类中还是以对象方法的方法存在

2.6 查看类方法/对象方法在类/元类中的IMP

void gomuIMP_classToMetaclass(Class pClass){
    
    //: 获取元类
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    //: 在类中获取对象方法`sayNO`的IMP
    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayNO));
    //: 在元类中获取对象方法`sayNO`的IMP
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayNO));
    //: 在类中获取类方法`sayLove`的IMP
    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayLove));
    //: 在元类中获取类方法`sayLove`的IMP
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayLove));

    GOMULog(@"%p - %p - %p - %p",imp1,imp2,imp3,imp4);

//: 调用
gomuIMP_classToMetaclass(pClass);

//: 打印
0x100001b90 - 0x1002c4140 - 0x1002c4140 - 0x100001b80
}

结论:

  • 对象方法sayNOGomuPerson中找到了IMP
  • 对象方法sayNOGomuPerson的元类中找到了IMP
  • 类方法sayLoveGomuPerson的中找到了IMP
  • 类方法sayLoveGomuPerson的元类中找到了IMP

问题:imp1,imp4都能理解,imp2,imp3怎么也有IMP?

2.6.1 第一种方式探索:查看源码
IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

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

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

    // Translate forwarding function to C-callable external version
    //: 当没有找到IMP,会返回一个`_objc_msgForward`
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}
  • 从源码不难看出,IMP是肯定存在的,只是有可能这个IMP可能是_objc_msgForward类型
2.6.2 第一种方式探索:lldb调试
//: 下断点,分别打印
(lldb) p/x imp1
(IMP) $0 = 0x0000000100001b90 (GomuTest`-[GomuPerson sayNO])
(lldb) p/x imp2
(IMP) $1 = 0x00000001002c4140 (libobjc.A.dylib`_objc_msgForward)
(lldb) p/x imp3
(IMP) $2 = 0x00000001002c4140 (libobjc.A.dylib`_objc_msgForward)
(lldb) p/x imp4
(IMP) $3 = 0x0000000100001b80 (GomuTest`+[GomuPerson sayLove])

结论:

  • 对象方法的imp中可以拿到
  • 类方法imp元类中可以拿到
  • imp不会为空,为空会默认_objc_msgForward

三、拓展知识

  • imp : 函数指针地址
  • sel : 方法编号

你可能感兴趣的:(OC底层原理08-方法、属性、成员变量的归属)