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

一、准备工作

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