iOS底层原理_05:类的原理分析(下)

第五节课 类的原理分析(下)

成员变量和属性以及编码

成员变量VS实例变量VS属性变量

成员变量定义在{}号中的变量,如果变量的数据类型是一个类则称这个变量为实例变量,因为实例变量是成员变量的一种特殊情况,所以实例变量也是类内部使用的,无需与外部接触的变量,这个也就是所谓的类私有变量。实例变量+基本数据类型变量=成员变量。而属性变量是用于与其他对象交互的变量。属性变量的好处就是允许让其他对象访问到该变量(因为属性创建过程中自动产生了set 和get方法)。

接下来我们通过代码来进行查看


05-添加copy属性.png

main转cpp文件
clang -rewrite-objc main.m -o main.cpp

在查看.cpp文件的时候我们发现同样都是属性,但是有的set方法是objc_setProperty,有的是self+OBJC_IVAR 这是为什么?接下来我们就来探索下

setter方法底层原理

给成员变量赋值的过程实际就是set方法,底层就是objc_setPropety,但其实它也只是一个中间层代码,具体是操作是在LLVM来进行的。这里就直接说结果了,为了每一个属性在使用setter方法的时候不必为其在底层创建单独的实现方法,当检测到属性对象存在copy属性的时候,会将此对象的setter方法重定向objc_setProperty(),然后只要在底层实现setProperty方法就可以将属性对象的setter方法实现了。

还是通过上面的cpp文件,我们来看下.cpp文件中的get、set方法


05-cpp文件验证.png

可以看到,存在copy属性的firstName、secondName都是使用的setProperty

接下来进入LLVM查看底层原理
全局搜索objc_setProperty,并找到如下代码

05-objc_setProperty.png

这里就是创建objc_setProperty的函数咯,但是我们发现这个方法并不是上层调用的,证明在创建之前还有一些底层方法,我们逐级反向推导一下
05-GetPropertySetFunction.png

调用getSetPropertyFn()的中间层是GetPropertySetFunction(),因为需要判断是否走getSetPropertyFn(),加一个中间层过度。全局搜索GetPropertySetFunction()
05-调用GetPropertySetFunction().png

根据switch条件PropertyImplStrategy类型调用GetPropertySetFunction()PropertyImplStrategy类型有两种GetSetProperty或者SetPropertyAndExpressionGet,下一步只要知道什么时候给策略赋值
05-PropertyImplStrategy.png

终于看到了原因,这块有这么一个判断。

小结:

  • copy修饰的属性使用objc_setProperty方式实现,其它属性使用内存偏移实现

  • 默认没有copy修饰的属性,默认使用的是strong,并不使用objc_setProperty

  • llvm源码验证流程:objc_setProperty -> getSetPropertyFn -> GetPropertySetFunction -> PropertyImplStrategy -> IsCopy(判断)

编码

在查看.cpp文件的时候我们会发现一些字符串形式的字母或单词,这个就叫做编码
想要查看到各种编码所代表的含义,我们可以通过Xcode的Developer Documentation -> 搜索getTypeEncoding -> Type Encodings或者直接点这里

举个例子:
@16@0:8

  • @:id类型
  • 16:所占用的内存
  • @:id self
  • 0:从0号位置开始
  • ::SEL
  • 8:从8号位置开始

类方法的存储

在上一篇文章中,我们通过bits/元类bits -> methods() -> list找到了方法列表,但是发现我们的对象方法存储在类里面,而类方法存储在元类里面,这样是为了让编译器能更好的识别方法名称,因为在底层C/C++函数眼中不会区分对象方法跟类方法,所以苹果单独创建一个元类用来存储类方法。

API方式解析

除了LLDB调试,我们还可以使用API进行打印:


05-LGPerson.png

先定义了这些东东,接下来我们来看到代码

1.通过获取方法列表验证

void hzmObjc_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));
        
        NSLog(@"Method, name: %@", key);
    }
    free(methods);
}

//主函数调用
 LGPerson *person = [LGPerson alloc];
 Class pClass     = object_getClass(person);
 hzmObjc_copyMethodList(pClass);
 
<----输出结果---->
Method, name: sayHello
Method, name: obj
Method, name: setObj:
Method, name: .cxx_destruct
Method, name: name
Method, name: setName:

没有打印我们的类方法sayHappy,意料之中是不是。根据上篇文章,我们知道类方法实际是在元类中的,那么我们就去拿到元类,传给hzmObjc_copyMethodList

//主函数调用
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
NSLog(@"*************");
hzmObjc_copyMethodList(metaClass);

最终打印结果如下图


05-打印结果.png

2.通过实例化方法验证

void hzmInstanceMethod_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));
    
    NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

//主函数调用
LGPerson *person = [LGPerson alloc];
Class pClass     = object_getClass(person);
hzmInstanceMethod_classToMetaclass(pClass);

<----输出结果---->
hzmInstanceMethod_classToMetaclass - 0x1000081c0-0x0-0x0-0x100008158

3.通过类方法验证

void hzmClassMethod_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));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}


//主函数调用
LGPerson *person = [LGPerson alloc];
Class pClass     = object_getClass(person);
hzmClassMethod_classToMetaclass(pClass);

<----输出结果---->
hzmClassMethod_classToMetaclass-0x0-0x0-0x100008148-0x100008148

看到这里,稍微有点疑惑,在类里面有类方法我们理解,但是元类里面为什么也有?好像有点懵,因为这里是直接class_getClassMethod,获取类方法,所以跟我们之前说的类里面存储对象方法不一样的哦~

这块就需要我们再次掏出我们的大宝剑,objc源码进行源码探索class_getClassMethod这个方法了

05-class_getClassMethod.png

我们可以看到在底层,实际调用的居然是class_getInstanceMethod,是获取对象方法,这也就印证我们之前说的*底层没有对象方法、类方法区分,都是对象方法

05-getMeta().png

而参数getMeta()内容如上图,实际判断如果是元类就直接返回了,所以class_getClassMethod的实际操作就是拿元类的对象方法,也就是 class_getInstanceMethod(metaClass)与上一条获取的一样,所以才会有输出

4.通过IMP方法验证

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

    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));// 0
    // sel -> imp 方法的查找流程 imp_farw
    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); // 0
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

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

}

//主函数调用
LGPerson *person = [LGPerson alloc];
Class pClass     = object_getClass(person);
hzmIMP_classToMetaclass(pClass);

<----输出结果---->
0x100003b20-0x7fff698cd580-0x7fff698cd580-0x100003b60
hzmIMP_classToMetaclass

我们发现元类的class_getMethodImplementation(metaClass, @selector(sayHello));存在,类的class_getMethodImplementation(pClass, @selector(sayHappy));存在,而且地址还一样。

05-class_getMethodImplementation.png

我们可以看到就算 imp 不存在,就会返回 _objc_msgForward

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容