我们将从_objc_init
开始
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
environ_init()
:环境变量的初始化。
tls_init()
:当前线程key的绑定。比如每个线程数据的析构函数;
static_init()
:运行C++静态构造函数。
这个时候我们如果打个断点,会看到这个 count 为 11 ,那么,这个 11 里面是否包含我们写的方法个数嘛?显然不是,它只是跟系统级别的构造函数有关。
lock_init()
:是空实现。可能是预留、未开源、工厂重写,或者能够跟 C++ 的通用。
exception_init()
:初始化libobjc
的异常处理系统,注册相应异常的回调函数,监控回调。
介绍了这么多的前戏,也该我们的主角_dyld_objc_notify_register(&map_images, load_images, unmap_image);
登场了。
其中_dyld_objc_notify_register
仅供Objc运行时使用,注册处理程序,以便在映射、取消映射和初始化Objc图像时调用。
&map_images
:映射镜像文件,dyld将使用包含objc-image-info
的镜像文件的数组回调mapped
函数。
void map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
if (firstTime) {
preopt_init();
}
hCount = 0;
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[i];
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
continue;
}
if (mhdr->filetype == MH_EXECUTE) {
#if __OBJC2__
size_t count;
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
_getObjc2MessageRefs(hi, &count);
selrefCount += count;
#else
_getObjcSelectorRefs(hi, &selrefCount);
#endif
}
hList[hCount++] = hi;
}
}
if (firstTime) {
sel_init(selrefCount);
arr_init();
}
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
这里你会发现和源码不太一样,主要是做了一些精简,我们只要知道一段代码主要的内容一般是在if else
判断、while
、do while
循环等,或者有 通过返回值来确定这个函数的功能 的代码块。毕竟 快速筛选核心代码才是王道。
这块代码里主要是计算出当前的count,然后通过_read_images
读镜像文件。
那么_read_images
里面会做些什么呢?
1、加载所有类到类的
gdb_objc_realized_classes
表中。
2、对所有类做重映射。
3、将所有SEL都注册到namedSelectors
表中。
4、修复函数指针遗留。
5、将所有Protocol
都添加到protocol_map
表中。
6、对所有Protocol
做重映射。
7、初始化所有非懒加载的类,进行rw、ro
等操作.
8、遍历已标记的懒加载的类,并做初始化操作。
9、处理所有Category
,包括Class
和Meta Class
。
10、初始化所有未初始化的类。
从宏观角度来分析,可分为以下七大部分
1、第一次进来,需要一个容器来存储数据,所以开始创建表
NXCreateMapTable
:所有类的表 -- 包括实现和没实现的
NXCreateHashTable
:记录已经开辟后的类(和元类)的表
两张表的目的:有时候有些未初始化的类在当前的表(小)里找不到,尽管它在总表里面有加载分配,但它并不一定是已经初始化的表,主要是为了断开。为了能够精确使用,不用每次从总表里查找。
2、类处理
3、方法编号处理
4、协议处理
5、非懒加载处理
6、待处理的类
7、分类处理
void _read_images {
// 1:第一次进来 - 开始创建表
// gdb_objc_realized_classes : 所有类的表 - 包括实现的和没有实现的
// allocatedClasses: 包含用objc_allocateClassPair分配的所有类(和元类)的表。(已分配)
if (!doneOnce) {
doneOnce = YES;
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
// 2:类处理
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
}
// 3: 方法编号处理
for (EACH_HEADER) {
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
// 4: 协议处理
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
NXMapTable *protocol_map = protocols();
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
// 5: 非懒加载类处理
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
addClassTableEntry(cls);
realizeClassWithoutSwift(cls);
}
// 6: 待处理的类
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls);
cls->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
// 7:分类处理
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
}
}
}
Class cls = (Class)classlist[i];
得到的cls
是个地址,此时还没有类,只是从map中读取到这个类占用的地址,但是这个地址指向的空间还未确定。
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
的readClass
中 没有 对ro、rw
进行处理,只是加入到总表里,并且因为已经分配了地址,所以也插入到HashTable
中。添加过的不会再次添加,readClass
的关键代码如下:
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
realizeClassWithoutSwift
:只是对rw
中的ro
进行赋值,rw
还是没处理。如图所示:
然后通过以下代码进行当前类的父类、元类
进行实现( 递归 )。当是supercls
时, 下次 的父类、元类
通过addSubclass
集成对等关系(双向链表)
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
cls->superclass = supercls;
cls->initClassIsa(metacls);
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
父类和元类.png
最后通过methodizeClass
这个真正的对rw
进行处理,通过attachLists
将ro
中的复制到rw
中。那么问题来了!
为什么已经有了ro
还要rw
呢?
ro
是万年不变的代码,只能够读取。但是有时候我们要进行外部的调试处理,这时我们就需要一个rw
能够进行动态的添加处理。
attachLists
是什么情况才会执行呢?
1、添加方法
addMethods
2、添加属性_class_addProperty
3、添加协议class_addProtocol
4、分类的加载attachCategories
attachLists
具体是怎么实现?
多对多
// many lists -> many lists
uint32_t oldCount = array()->count;//10
uint32_t newCount = oldCount + addedCount;//4
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;// 10+4
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
0对一
// 0 lists -> 1 list
list = addedLists[0];
一对多:
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
其中memcpy
快,memmove
慢。
处理时是将一维数组method_t
打包成method_list_t
直接插到二维数组method_array_t
,方便直接。
总结
1、readclass:判断是不是后期要处理的类,插入到表;
2、realizeClassWithoutSwift:读取class的data()
-> ro/rw创建
-> 父类与元类的实现
-> 父类与元类的归属关系
-> 将此类链接到其超类的子类列表
;
3、methodizeClass:把ro
的数据写入到rw
;
4、attachLists:有一对多
、0对一
、多对多
三种情况。
网友评论