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

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方法

可以看到,存在copy属性的firstName、secondName都是使用的setProperty
接下来进入LLVM查看底层原理
全局搜索objc_setProperty,并找到如下代码

这里就是创建
objc_setProperty的函数咯,但是我们发现这个方法并不是上层调用的,证明在创建之前还有一些底层方法,我们逐级反向推导一下
调用
getSetPropertyFn()的中间层是GetPropertySetFunction(),因为需要判断是否走getSetPropertyFn(),加一个中间层过度。全局搜索GetPropertySetFunction()
根据
switch条件PropertyImplStrategy类型调用GetPropertySetFunction(),PropertyImplStrategy类型有两种GetSetProperty或者SetPropertyAndExpressionGet,下一步只要知道什么时候给策略赋值
终于看到了原因,这块有这么一个判断。
小结:
copy修饰的属性使用objc_setProperty方式实现,其它属性使用内存偏移实现默认
没有copy修饰的属性,默认使用的是strong,并不使用objc_setPropertyllvm源码验证流程:
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进行打印:

先定义了这些东东,接下来我们来看到代码
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);
最终打印结果如下图

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这个方法了

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

而参数
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));存在,而且地址还一样。

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