本系列博客是本人的源码阅读笔记,如果有iOS开发者在看runtime的,欢迎大家多多交流。为了方便讨论,本人新建了一个微信群(iOS技术讨论群),想要加入的,请添加本人微信:zhujinhui207407,【加我前请备注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,欢迎一起讨论
本文完整版详见笔者小专栏:https://xiaozhuanlan.com/runtime
分析
上一篇文章我们说过header_info
封装了headerType
,后者前面的文章已经说过了,其实是mach_header_64
类型,封装函数如下:
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
其实现如下(去掉部分冗余逻辑):
static header_info * addHeader(const headerType *mhdr, const char *path, int &totalClasses, int &unoptimizedTotalClasses)
{
header_info *hi;
if (bad_magic(mhdr)) return NULL;
bool inSharedCache = false;
// Look for hinfo from the dyld shared cache.
hi = preoptimizedHinfoForHeader(mhdr);
if (hi) {
// Found an hinfo in the dyld shared cache.
// Weed out duplicates.
if (hi->isLoaded()) {
return NULL;
}
inSharedCache = true;
// Initialize fields not set by the shared cache
// hi->next is set by appendHeader
hi->setLoaded(true);
}
else
{
// Weed out duplicates
for (hi = FirstHeader; hi; hi = hi->getNext()) {
if (mhdr == hi->mhdr()) return NULL;
}
// Locate the __OBJC segment
size_t info_size = 0;
unsigned long seg_size;
const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
const uint8_t *objc_segment = getsegmentdata(mhdr,SEG_OBJC,&seg_size);
if (!objc_segment && !image_info) return NULL;
// Allocate a header_info entry.
// Note we also allocate space for a single header_info_rw in the
// rw_data[] inside header_info.
hi = (header_info *)calloc(sizeof(header_info) + sizeof(header_info_rw), 1);
// Set up the new header_info entry.
hi->setmhdr(mhdr);
// Install a placeholder image_info if absent to simplify code elsewhere
static const objc_image_info emptyInfo = {0, 0};
hi->setinfo(image_info ?: &emptyInfo);
hi->setLoaded(true);
hi->setAllClassesRealized(NO);
}
{
size_t count = 0;
if (_getObjc2ClassList(hi, &count)) {
totalClasses += (int)count;
if (!inSharedCache) unoptimizedTotalClasses += count;
}
}
appendHeader(hi);
return hi;
}
这个函数其实很好理解:
- 判断一下当前的header在dyld的共享缓存中有没有
- 如果有的话直接设置已加载
- 如果共享缓存中没有,那么就实行“封装操作”
- 封装成功以后,加入到链表中。
现在我们一步步分析:
判断当前的header在dyld的共享缓存中有没有
对应的方法是:
hi = preoptimizedHinfoForHeader(mhdr);
其实现如下(去掉部分冗余逻辑):
header_info *preoptimizedHinfoForHeader(const headerType *mhdr)
{
objc_headeropt_ro_t *hinfos = opt ? opt->headeropt_ro() : nil;
if (hinfos) return hinfos->get(mhdr);
else return nil;
}
不难看出,这个共享缓存的数据都在opt
内,我们推测opt
应该是一个全局或者静态变量,点击进入看一下其声明以及定义:
// preopt: the actual opt used at runtime (nil or &_objc_opt_data)
// _objc_opt_data: opt data possibly written by dyld
// opt is initialized to ~0 to detect incorrect use before preopt_init()
static const objc_opt_t *opt = (objc_opt_t *)~0;
果然是个静态变量。
~0
这个之前笔者已经介绍过了,其实就是0Xffffffff
,这是块安全区域,防止进入其他位置导致野指针。上面的注释也大概介绍了opt
初始化时机:方法preopt_init()
中。
那调用时机是在哪里呢,见下图:
该方法的实现这里笔者就不展开讲了,有兴趣的读者可以自行参阅。
opt
的定义也不复杂,代码拷贝如下:
struct alignas(alignof(void*)) objc_opt_t {
uint32_t version;
uint32_t flags;
int32_t selopt_offset;
int32_t headeropt_ro_offset;
int32_t clsopt_offset;
int32_t protocolopt_offset;
int32_t headeropt_rw_offset;
const objc_selopt_t* selopt() const {
if (selopt_offset == 0) return NULL;
return (objc_selopt_t *)((uint8_t *)this + selopt_offset);
}
objc_selopt_t* selopt() {
if (selopt_offset == 0) return NULL;
return (objc_selopt_t *)((uint8_t *)this + selopt_offset);
}
struct objc_headeropt_ro_t* headeropt_ro() const {
if (headeropt_ro_offset == 0) return NULL;
return (struct objc_headeropt_ro_t *)((uint8_t *)this + headeropt_ro_offset);
}
struct objc_clsopt_t* clsopt() const {
if (clsopt_offset == 0) return NULL;
return (objc_clsopt_t *)((uint8_t *)this + clsopt_offset);
}
struct objc_protocolopt_t* protocolopt() const {
if (protocolopt_offset == 0) return NULL;
return (objc_protocolopt_t *)((uint8_t *)this + protocolopt_offset);
}
struct objc_headeropt_rw_t* headeropt_rw() const {
if (headeropt_rw_offset == 0) return NULL;
return (struct objc_headeropt_rw_t *)((uint8_t *)this + headeropt_rw_offset);
}
};
看名字就很容易理解,分别是
- 类的缓存
clsopt()
- 协议的缓存
protocolopt()
- 头的缓存
headeropt_rw()
- 选择器的缓存
selopt()
有则直接设置已加载
对应的代码:
if (hi) {
// Found an hinfo in the dyld shared cache.
// Weed out duplicates.
if (hi->isLoaded()) {
return NULL;
}
inSharedCache = true;
// Initialize fields not set by the shared cache
// hi->next is set by appendHeader
hi->setLoaded(true);
}
对,很好理解,唯一有问题的点在于方法isLoaded()
:
bool isLoaded() {
return getHeaderInfoRW()->getLoaded();
}
继续进入该方法:
header_info_rw *getHeaderInfoRW() {
header_info_rw *preopt =
isPreoptimized() ? getPreoptimizedHeaderRW(this) : nil;
if (preopt) return preopt;
else return &rw_data[0];
}
我们查看isPreoptimized()
可以发现:
/***********************************************************************
* Return YES if we have a valid optimized shared cache.
**********************************************************************/
bool isPreoptimized(void)
{
return preoptimized;
}
而preoptimized
定义如下:
static bool preoptimized;
这也是个静态变量。和前面的opt
一样!也就是说dlyd的共享缓存其实是由两个变量来决定的一个opt
存放动态缓存数据,另一个preoptimized
存放标志位。
如果共享缓存中没有,那么就实行“封装操作”
这一步操作有点复杂,这里先不做介绍了,后面的文章着重分析。
封装成功以后,加入到链表中
appendHeader(hi);
其具体实现如下:
/***********************************************************************
* appendHeader. Add a newly-constructed header_info to the list.
**********************************************************************/
void appendHeader(header_info *hi)
{
// Add the header to the header list.
// The header is appended to the list, to preserve the bottom-up order.
HeaderCount++;
hi->setNext(NULL);
if (!FirstHeader) {
// list is empty
FirstHeader = LastHeader = hi;
} else {
if (!LastHeader) {
// list is not empty, but LastHeader is invalid - recompute it
LastHeader = FirstHeader;
while (LastHeader->getNext()) LastHeader = LastHeader->getNext();
}
// LastHeader is now valid
LastHeader->setNext(hi);
LastHeader = hi;
}
}
从以上代码看出header_info
原来还是个链表:
//获取下一个data:
header_info *getNext() {
return getHeaderInfoRW()->getNext();
}
//设置下一个数据
void setNext(header_info *v) {
getHeaderInfoRW()->setNext(v);
}
怎么样,读者朋友们是不是对header_info
有了更深的了解。
总结
本文主要介绍了方法addheader
,调用栈位于:
_objc_init
|-dyld_objc_notify_register
|-map_2_images
|-map_images_nolock
|-addHeader
当然,本文也顺便带出了dyld的共享缓存在runtime中的使用。虽然代码量有点多,但思路应该很清晰了,希望带给大家一些启发。
网友评论