打个符号断点:_objc_init
,可以看到以下调用栈
1. dyld 链接动态库
app在被打开的时候所依赖的动态库会被加载到程序中.
dyld(the dynamic link editor)
这样一部手机所有app都共用系统的动态库,这样做大大的减小了可执行文件(ipa)大小.
将模拟器路径上的PGRuntimeTrain.app ShowInFinder,然后:
otool -L PGRuntimeTrain.app/PGRuntimeTrain
PGRuntimeTrain.app/PGRuntimeTrain:
/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1349.54.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.dylib (compatibility version 1.0.0, current version 1238.50.2)
/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 1349.55.0)
/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 3600.7.47)
Foundation.framework
CoreFoundation.framework
UIKit.framework
libobjc.A.dylib
├──objc
└──runtime
libSystem.dylib
├── libdispatch//GCD
├── libsystem_c//C语言库
├── libsystem_blocks//Block
└── libcommonCrypto//加密库,比如常用的 md5 函数
在做一些"逆"的工作时在这个环节可以加入自己写的.dylib
,功力不够就不细说了
2. ImageLoader 加载镜像文件
动态库加载完成后就该加载我们自己的编写的代码编译成的二进制文件了,就是ImageLoaderXXXXXX
系列方法.这些image内就编译着我们自己写的符号、代码等.
3. _objc_init runtime初始化
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
//读取镜像前,基本环境的初始化
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_2_images, load_images, unmap_image);
}
3.1 map_2_images
map_2_images.pngmap_2_images
└─map_images_nolock
└─_read_images
- map_images_nolock
map_images_nolock内的已经完成了:
header_info *hList[mhCount];==>类信息读取到header_info
的链表数组
preopt_init==>优化共享缓存的初始化
sel_init==>初始化方法列表
arr_init==>初始化自动释放池+散列表
- _read_image
到_read_image
方法内所有类的信息全放在header_info
的链表数组之中,以该链表数组出发:
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
"IMAGE TIMES: first time tasks”—>support indexed_isa
"IMAGE TIMES: discover classes”—>_getObjc2ClassList
"IMAGE TIMES: remap classes”—>_getObjc2ClassRefs
"IMAGE TIMES: fix up selector references”—>_getObjc2SelectorRefs
"IMAGE TIMES: fix up objc_msgSend_fixup”—>_getObjc2MessageRefs
"IMAGE TIMES: discover protocols”—>_getObjc2ProtocolList
"IMAGE TIMES: fix up @protocol references”—>_getObjc2ProtocolRefs
"IMAGE TIMES: realize non-lazy classes”—>_getObjc2NonlazyClassList
"IMAGE TIMES: realize future classes”—>resolvedFutureClasses
"IMAGE TIMES: discover categories”—>_getObjc2CategoryList
-
1.确认是否支持indexed_isa
-
2.读取所有类信息
classref_t *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
当调用readClass
方法返回的Class newCls
与传入的cls
不一样时,则会将Class newCls
加了数组Class *resolvedFutureClasses
,方便后面对类做实现(第6步).
-
3.读取所有方法信息
-
4.读取所有协议信息
-
5.实现所有的
non-lazy classes
注意第2步:读取所有类信息内用的是:
classref_t *classlist = _getObjc2ClassList(hi, &count);
而现在用的是:
classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
实现了+load
的类就是non-lazy classes
,没有实现+load
的类就是lazy classes
realizeClass//cls->ro到cls->rw->ro
└─methodizeClass//向cls->rw内填充方法列表,属性列表,协议列表
└─attachCategories//读取类对应分类的内容
5.1 realizeClass
realizeClass除了实现本类外,还实现本类的父类和元类.在此之前,cls->data()
指向的是ro
,在realizeClass
内,完成了cls->ro
到cls->rw->ro
的转换(字面也能看出来:ro->readonly
,rw->readwrite
,之后rw
内就可以写入内容了)
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
5.2 methodizeClass
向cls->rw
内填充方法列表,属性列表,协议列表
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
5.3 attachCategories
读取类对应分类内的方法列表,属性列表,协议列表,填入cls->rw
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
while (i--) {
auto& entry = cats->list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
rw->properties.attachLists(proplists, propcount);
free(proplists);
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
- 6.resolvedFutureClasses
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
realizeClass(resolvedFutureClasses[i]);
resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
将第2步readClass
中产生的Class *resolvedFutureClasses
内的类全部初始化.
- 7.读取所有分类信息
可能你会说不是在attachCategories
方法内读取过类对应分类的消息然后填入cls->rw
吗?
以上已经说过了realizeClass
初始化的是non-lazy class
(实现了+load
方法的类),所以还是有许多lazy class
没有初始化.
category_t **catlist = _getObjc2CategoryList(hi, &count);
Class cls = remapClass(cat->cls);
读取分类列表,由单个分类(cat)
反拿所属类(cls)
,
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
7.1 addUnattachedCategoryForClass
绑定类与分类的关系.
7.2 如果满足(cat->instanceMethods||cat->protocols||cat->instanceProperties)
,则看类有没有被初始化cls->isRealized()
,初始化了则调用remethodizeClass
.
remethodizeClass
比methodizeClass
简单了很多,因为调用remethodizeClass
方法的情况下,类肯定是已经被初始化了.当然remethodizeClass
最后也调用了attachCategories
.
cat->instanceMethods
cat->protocols
cat->instanceProperties
以上三项对应类;
cat->classMethods
cat->protocols
cat->_classProperties
以上三项则对应类的元类;
当然调用addUnattachedCategoryForClass
与remethodizeClass
的逻辑,类与元类是一样的.
存疑: realizeClass->methodizeClass->attachCategories
discover categories->remethodizeClass->attachCategories
two way
way1:初始化所有非懒加载类->填充3大列表->取出分类再填充3大列表
way2:拿出所有分类->反拿分类的类->类已经初始化->取出分类再填充3大列表
好像做了重复的工作,存疑
3.2 load_images
load_images.pngvoid load_images(const char *path __unused, const struct mach_header *mh)
{
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
rwlock_writer_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
load_images
的功能会比map_2_images
简明很多,就是调用所有实现+load
方法的类与分类.
- prepare_load_methods(生产)
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertWriting();
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
- 生产类
注意这边获取non_lazy class
列表的方法与先前讲过获取方法已经不一样了:
classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
extern classref_t *_getObjc2NonlazyClassList(const header_info *hi, size_t *count);
classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
extern classref_t *_getObjc2NonlazyClassList(const headerType *mhdr, size_t *count);
传入的值由header_info
变成了headerType
.
在获取所有non_lazy class
后,调用schedule_class_load
,将
non_lazy class
加入静态结构体数组static struct loadable_class *loadable_classes;
struct loadable_class {
Class cls; // may be nil
IMP method;
};
//cls->对应类
//method->+load方法的地址
schedule_class_load
内有一个特殊处理:就是用类的父类递归调用schedule_class_load
,这样数组loadable_classes
内父类就排在前边,父类的+load
自然也就先调用.
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
- 生产分类
处理好实现了+load
的类后就是要处理实现了+load
的分类了.
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
void add_category_to_loadable_list(Category cat)
{
IMP method;
loadMethodLock.assertLocked();
method = _category_getLoadMethod(cat);
// Don't bother if cat has no +load method
if (!method) return;
if (PrintLoading) {
_objc_inform("LOAD: category '%s(%s)' scheduled for +load",
_category_getClassName(cat), _category_getName(cat));
}
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *)
realloc(loadable_categories,
loadable_categories_allocated *
sizeof(struct loadable_category));
}
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
获取所有实现+load
的分类:
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
加入静态结构体数组static struct loadable_category *loadable_categories;
struct loadable_category {
Category cat; // may be nil
IMP method;
};
//cat->对应分类
//method->+load方法的地址
- call_load_methods(消费)
在做完前面的生产准备工作之后,就是要消费准备好的类和分类了.
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
printf("call_load_methods up \n");
do {
printf("call_load_methods in \n");
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
printf("call_load_methods down \n");
objc_autoreleasePoolPop(pool);
loading = NO;
}
顺序是先类后分类,所以+load
方法的调用顺序可以总结为父类->类->分类
.
- 消费类
call_class_loads
遍历准备好的loadable_classes
内的每一个struct loadable_class
,调用(*load_method)(cls, SEL_load);
- 消费分类
call_category_loads
遍历准备好的loadable_categories
内的每一个struct loadable_category
,调用(*load_method)(cls, SEL_load);
3.3 unmap_image
跑了源码没有调用过unmap_image
,想来是针对一些读取镜像失败的特殊情况的
在runtime初始化完成之际可以看到只有non_lazy class
加载好了,其他的类还没加载到内存中,那这些类有事什么时候才加载那内存中呢?这些类会在程序第一次用到这些类的时候才初始化到内存中,类的+initialize
方法也就是在此时被调用的.
所有流程可以合并以下流程图:
_objc_init.png
文章参考:
objc源码
我们的对象会经历什么
网友评论