在 WWDC2020中有一个视频讲的是关于Objective—C 在运行时的一些优化,本篇文章是对视频中讲到的的部分知识点进行探索。
如果感兴趣的可以去看一下这个视频,或者看我上一篇文章:关于WWDC2020-Objective-C运行时的改进。
对视频中讲到的几个点做验证:
1.class_ro_t 存储着 Flags,Size,Name,Methods,Protocols,Ivars,Properties。
2.class_rw_t 存储着 Methods,Protocols,Properties。
3.为什么 class_ro_t 已经存储了 Methods,Protocols,Properties,而 class_rw_t 还要存储这些?
WWDC 中的介绍是 class_ro_t 是只读的,存储的信息在编译时期就已经确定了,不能再更改,并且必要的时候可以清除,需要用的时候重磁盘中加载就好了。class_rw_t 是可读可写的,它信息的存储是在运行的时候,需要用到的时候才会写入,并且一直存在于内存中。
其中,第2点我们在前面的篇章中已经验证过了,确实有方法列表,协议列表和属性列表,但协议列表没有去验证,看完下面的验证方法后,再去验证class_rw_t的协议方法,也是一样的。
我们先声明几个类,分别为继承自 NSObject 的 SHPerson,SHPerson 的分类 SHPerson(Home),和继承自 SHPerson 的 SHTeacher。
#pragma mark: - SHPerson
@interface SHPerson : NSObject
{
NSString *_name;
NSString *_age;
NSObject *family;
}
@property (nonatomic, copy) NSString *nickname;
@end
@implementation SHPerson
- (void)play_basketball {}
+ (void)playFootball {}
@end
#pragma mark: - SHPerson 的分类
@interface SHPerson(Home)
- (void)write_homework;
+ (void)eat;
@end
@implementation SHPerson(Home)
- (void)write_homework {}
+ (void)eat {}
@end
#pragma mark: - SHTeacher
@interface SHTeacher : SHPerson
@property (nonatomic, strong) NSString *course;
@end
@implementation SHTeacher
- (void)attend_class {}
+ (void)change_homework {}
@end
(滑动显示更多)
在 class_rw_t 中,有这么一这个方法:
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
(滑动显示更多)
通过 ro() 这个方法可以拿到 class_ro_t 的数据,我们来看一下。
class_ro_t 中有 Flags,Size,Name,Methods,Protocols,Ivars,Properties 的成员变量,但是 baseProtocols 为nil,我们向 SHPreson 中添加协议,再运行看看。
#pragma mark: - Protocol
@protocol SHPersonProtocol<NSObject>
- (void)run;
- (void)sleep;
+ (void)work;
@end
#pragma mark: - SHPerson
@interface SHPerson : NSObject<SHPersonProtocol>
{
NSString *_name;
NSString *_age;
NSObject *family;
}
@property (nonatomic, copy) NSString *nickname;
@end
@implementation SHPerson
- (void)play_basketball {}
+ (void)playFootball {}
/// SHPersonProtocol
- (void)run {}
- (void)sleep {}
+ (void)work {}
@end
(滑动显示更多)
我们来重新打印一下:
baseProtocols 有值了,我们再来看一下源码中 class_ro_t 的结构。
我们先来看一下class_ro_t 的 ivars 中都有什么:
确实有 SHPerson 中的四个成员变量,那如果我们在分类中添加属性或者成员变量,class_ro_t 的 ivars 和 baseProperties 是否也会存储呢?
可以看到在分类中添加实例变量语法都不过,那我们只能添加属性了,但其实大概也能猜出来添加属性就算语法过了,class_ro_t 中也不会存储。先来验证一下吧。
在分类中添加属性还要求添加相对应的 getter 和 setter。
准备好了之后,我们来验证一下:
并没有 height 属性,我们的猜想正确!这也是分类中不能添加属性的原因,如果想要添加属性,并且能正常使用,需要用到关联对象方法。
我们来看一下 baseMethodList 的内存结构:
我们发现 baseMethodList 是一个 void *const 的类型。还记得前面学过的,方法列表的类型吗?method_list_t,我们把 void *const 转成 method_list_t * 。然后打印出 method_list_t 中 get 返回的 method_t 的 big() 方法。
我们发现报错了,来看一下 big() 的实现:
big &big() const {
ASSERT(!isSmall());
return *(struct big *)this;
}
(滑动显示更多)
再来看一下 baseMethodList 在源码中的注释:
大概的意思是:如果它指向一个小列表,则这是有符号的,但是如果它指向一个大列表,则可能是无符号的。
那既然 big() 不行,通过 name() 打印试试。
这个时候会发现,方法名称打印出来了,并且baseMethodList 还打印出了包括分类和协议中的所有实例方法!
接下来,我们再来看一下 baseProtocols 中是否也有我们相对应的协议方法。
这么一看好像看不出什么,我们来看下protocol_list_t的源码:
struct protocol_list_t {
// count is pointer-sized by accident.
uintptr_t count;
protocol_ref_t list[0]; // variable-size
size_t byteSize() const {
return sizeof(*this) + count*sizeof(list[0]);
}
protocol_list_t *duplicate() const {
return (protocol_list_t *)memdup(this, this->byteSize());
}
typedef protocol_ref_t* iterator;
typedef const protocol_ref_t* const_iterator;
const_iterator begin() const {
return list;
}
iterator begin() {
return list;
}
const_iterator end() const {
return list + count;
}
iterator end() {
return list + count;
}
};
(滑动显示更多)
源码中也看不出什么,但是这个 list 在源码中是 list[0],我们把 list[0] 打印出来试试:
打印出来是protocol_ref_t,我们再去看源码中 protocol_ref_t 是什么结构:
我们发现,protocol_ref_t 后面有个注释,protocol_ref_t 是一个 protocol_t * 类型的,我们来看一下 protocol_t 在源码中的结构:
这不就是我们想要的吗,接下来通过 lldb 将 protocol_ref_t 强转成 protocol_t * 并打印:
完全有我们想要的东西,接下来就是一顿操作:
通过验证,baseProtocols 确实存着协议方法。
接下来,我们再来看一下 baseProperties 中是否也有我们相对应的属性信息。
不仅有 nickname 这个属性,还存有分类中的 height 属性和系统的其它属性。
关于为什么 firstSubclass 为 nil 的问题,通过一段 lldb 打印来看一下:
class_rw_t 存储着类的实例方法,协议方法,属性相关的信息
class_ro_t 存储着 Flags(一些其它的数据,比如引用计数相关的),类的大小,类的名称,类的实例方法列表,协议方法列表,成员变量以及属性相关的信息。
来源:稀土掘金
作者:Coder_张三
链接:https://juejin.cn/post/7041911723128782855
- END -
本文分享自 HelloCoder全栈小集 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!