这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
首先看下苹果WWDC2020关于runtime优化视频讲解
一、类内存的ro数据
上一篇章分析到类的属性和方法,那么成员变量呢?
通过打印
class_rw_t的properties()发现只有类的属性但没有成员变量,我们通过WWDC2020关于runtime优化的视频讲解中了解到成员变量在ro中。而class_rw_t结构体中可以看到有ro的get方法
打印class_rw_t中的ro,得到了class_ro_t。class_ro_t中可以看到有个ivars属性,打印出来得到一个ivar_list_t
接着通过
get方法就可以发现所有成员变量
二、成员变量和属性以及编码
1、成员变量和属性
上一节找到了成员变量,成员变量和属性在底层中是怎么展现的呢?
通过clang命令编译成底层C++文件
可以看到在底层C++中没有属性,而全在
LRPerson_IMPL结构体中,并且属性都被转换成带_形式。
与此同时OC中的属性在底层C++中会生成
get和set方法,而成员变量不会
2、编码
在C++源码中,可以看到这些符号,这些符号是什么?代表什么意思呢?
这些都是编码,可以通过command + shift + 0,搜索ivar_getTypeEncoding,点击Type Encodings查看详细内存。附上Type Encodings 地址
以
"setName:", "v24@0:8@16"为例
1、v函数返回值, 表示void
2、24函数参数总共所占内存大小
3、@第一个参数(隐藏参数)即self
4、0从0号位置开始
5、:第二个参数(隐藏参数)SEL
6、8从8号位置开始
7、@第三个参数
8、16从16号位置开始
3、补充
实例变量:特殊的成员变量 (对象类型声明,类的实例化),非基本数据类
三、setter、getter方法的底层原理
1、setter方法
在刚才编译的底层C++文件中,可以看到OC中属性的get和set方法。但是仔细观察发现nickName的set方法是通过objc_setProperty实现的,而name和aname的set方法是通过内存平移赋值实现的
从llvm源码中查找objc_setProperty,发现在getSetPropertyFn中创建
继续找
getSetPropertyFn
继续找
GetPropertySetFunction
发现
GetPropertySetFunction的调用是在一个switch的case中,查看下PropertyImplStrategy类型
PropertyImplStrategy的实现
到此可以看到
IsCopy的判断,如果是IsAtomic则赋值为GetSetProperty否则为SetPropertyAndExpressionGet,所以copy是影响使用objc_setProperty的重要因素,通过下面代码验证一下
@property (nonatomic, copy) NSString *nickName;
@property (atomic, copy) NSString *nickName1;
@property (nonatomic) NSString *nickName2;
@property (atomic) NSString *nickName3;
用clang命令编译成C++源码,可以发现用copy修饰的属性底层通过objc_setProperty实现set方法,其他则通过内存平移赋值的方式
在objc源码中看下objc_setProperty函数的实现
可以看到底层会调用
copyWithZone。所以copy修饰的属性,是会复制内存的,其他修饰符通过内存平移则不会
2、getter方法
在探究set方法时,我们可以看到有些get方法通过objc_getProperty实现,而有些是直接return一个内存平移后的值
在
llvm中继续探索objc_getProperty,PropertyImplStrategy类型为GetSetProperty
最终影响
getter方法的决定性因素还是这段代码
这段代码的注释也写的很清楚
- 如果是
copy修饰的属性,用setProperty - 如果属性还用
atomic修饰,那么就用getProperty
四、类方法存储的API方式解析
一个类中有以下两个方法
- (void)sayHello;
+ (void)sayHappy;
1、class_getInstanceMethod
void lrInstanceMethod_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(@"%p - %p - %p - %p",method1,method2,method3,method4);
}
输出结果:
0x1000081c0 - 0x0 - 0x0 - 0x100008158
结果分析:
sayHello对象方法存在类中sayHappy类方法存在元类中
2、class_getClassMethod
void lrClassMethod_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(@"%p - %p - %p - %p",method1,method2,method3,method4);
}
输出结果:
0x0 - 0x0 - 0x100008158 - 0x100008158
class_getClassMethod是获取类方法,在底层中就是获取元类的对象方法
method4传入的是元类,底层代码中会判断若已是元类直接返回,否则返回元类
对象是类的实例,类是元类的实例,在底层中没有类方法这一说,只有对象方法
3、class_getMethodImplementation
void lrIMP_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));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p - %p - %p - %p",imp1,imp2,imp3,imp4);
}
输出结果:
0x100003ae0 - 0x7fff6e3b7580 - 0x7fff6e3b7580 - 0x100003b20
根据class_getInstanceMethod的分析,这里imp2 imp3应该没有实现,但是输出结果却有值。这里涉及到方法查找流程,即使找不到也会返回一个_objc_msgForward。关于消息转发机制后续再详细介绍
4、isKindOfClass、isMemberOfClass
void kindOfTest(void) {
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[LRPerson class] isKindOfClass:[LRPerson class]];
BOOL re4 = [(id)[LRPerson class] isMemberOfClass:[LRPerson class]];
NSLog(@" re1 :%d\n re2 :%d\n re3 :%d\n re4 :%d\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[LRPerson alloc] isKindOfClass:[LRPerson class]];
BOOL re8 = [(id)[LRPerson alloc] isMemberOfClass:[LRPerson class]];
NSLog(@" re5 :%d\n re6 :%d\n re7 :%d\n re8 :%d\n",re5,re6,re7,re8);
}
输出结果:
re1 :1
re2 :0
re3 :0
re4 :0
re5 :1
re6 :1
re7 :1
re8 :1
根据
isKindOfClass、isMemberOfClass的源码分析:
-
+ (BOOL)isKindOfClass:(Class)cls类方法。获取类的元类,判断元类和参数是否相同,不同则循环取父类判断。
re1中NSObject是根类,而根元类的父类就是根类和参数相同,返回YES。
re3中从LRPerson元类找到父类NSObject再到nil,都与参数LRPerson类不同。所以返回NO。 -
- (BOOL)isKindOfClass:(Class)cls对象方法。获取当前对象的类,判断类和参数是否相同,不同则循环取父类判断。
re5中NSObject对象的类和参数NSObject类相同,返回YES。
re7中LRPerson对象的类和参数LRPerson类相同,返回YES。 -
+ (BOOL)isMemberOfClass:(Class)cls类方法。获取类的元类,判断元类和参数是否相同
re2中NSObject元类和NSObject类不相同,返回NO。
re4中LRPerson元类和LRPerson类不相同,返回NO。 -
- (BOOL)isMemberOfClass:(Class)cls对象方法。获取对象的类,判断类和参数是否相同
re6中NSObject类和NSObject类相同,返回YES。
re8中LRPerson类和LRPerson类相同,返回YES。
通过汇编还可以发现isKindOfClass、isMemberOfClass在底层中走的是objc_opt_isKindOfClass