参考
dyld版本
objc4-750 (目前官方网址已经没有这个版本了)
源码
_objc_init
_objc_init会在dyld初始化libSystem库时调用,具体调用顺序看dyld源码阅读
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
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_images, load_images, unmap_image);
}
environ_init();
> 读取影响runtime的环境变量。如果需要,还可以打印环境变量。
> 终端输入export OBJC_HELP=1后回车,再输入ls,则可看到所有环境变量:例如OBJC_PRINT_LOAD_METHODS,用于打印所有+load方法
tls_init();
> 关于线程key的绑定
static_init();
> 运行C++ static constructor functions
> 在dyld调用我们的静态构造函数之前,libc 会调用_objc_init,所以这里我们必须自己来初始化内置的C++静态构造函数
> 疑问:这个_objc_init不是只能调用一遍吗?
lock_init();
> 空实现
exception_init();
> 初始化 libobjc 的异常处理系统
> 异常都会打到_objc_terminate函数里
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
> _dyld_objc_notify_register为dyld提供的API
> 注册dyld映射/加载/卸载镜像时的回调
_dyld_objc_notify_register
注册回调,dyld在映射镜像(map_images)、加载镜像(load_images)、卸载镜像(unmap_image)时进行调用。
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
dyld::registerObjCNotifiers(mapped, init, unmapped);
}
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
try {
notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
}
catch (const char* msg) {
// ignore request to abort during registration
}
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
}
}
mapped回调: 注册回调的时候会立即进行调用(notifyBatchPartial函数),对所有映射的镜像进行objc setup操作;在dyld源码中搜索(*sNotifyObjCMapped)(objcImageCount, paths, mhs);
init回调:注册的时候会对所有已初始化的镜像立即进行调用;另外会在镜像初始化时调用;在dyld源码搜索(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
unmapped回调:卸载时调用
具体流程看dyld源码阅读
map_images
调用时机:注册回调的时候会立即进行调用(notifyBatchPartial函数),对所有映射的镜像进行objc setup操作;在dyld源码中搜索(*sNotifyObjCMapped)(objcImageCount, paths, mhs);
重点流程:
map_images ——> map_images_nolock ——> _read_images
主要做了什么:
1.处理类、协议、selector及其引用
2.realizeClass:进行类的首次初始化,生成相应的类的结构体,包括生成rw信息。
1.生成存储类的全局哈希表
gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
-
gdb_objc_realized_classes存储的是不在dyld shared cache(动态库共享缓存)中的类,无论这个类是否realized实现。
全局搜索会发现, getClass_impl() addNamedClass()中有用到 -
allocatedClasses存储的是使用objc_allocateClassPair进行alloc的类。
objc_allocateClassPair补充
objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name, size_t extraBytes);
objc_allocateClassPair用于创建一个类和元类。
传入父类,返回子类。
可以通过object_getClass(newClass)返回元类,可以通过class_addMethod添加方法,class_addIvar添加实例变量。
注意,实例方法需要添加给类,类方法添加给元类。
2.discover classes
for (EACH_HEADER) {
if (! mustReadClasses(hi)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
// 从machO中获取所有class
classref_t *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
// 遍历所有类
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;
}
}
}
ts.log("IMAGE TIMES: discover classes");
对类进行处理, 其中, readClass方法中,会将class的只读信息rw->ro写到可读可写信息rw中,也就是将类本身具有的方法、属性、协议等写到可读可写信息中,而分类中的信息则会在后续步骤中添加进去。
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
// 省略...
// 将ro(类的只读信息)写到rw中
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro;
memcpy(newCls, cls, sizeof(objc_class));
rw->ro = (class_ro_t *)newCls->data();
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
// 省略...
}
3.remap classes
修复remapped类。类列表和非懒加载类列表保持unremapped。类引用和父类引用进行重映射。
如果一个类实现了 + load 方法,那这个类就是 non-lazy class(非懒加载类)。反之,没实现 + load 方法,就是懒加载类。
// Fix up remapped classes
// Class list and nonlazy class list remain unremapped.
// Class refs and super refs are remapped for message dispatching.
if (!noClassesRemapped()) {
for (EACH_HEADER) {
// 获取所有类引用
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
// 修复类引用,以防引用的类已重新分配
remapClassRef(&classrefs[i]);
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
4.fix up selector references
修正selector引用
// Fix up @selector references
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
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);
}
}
}
5.Fix up old objc_msgSend_fixup call sites
// Fix up old objc_msgSend_fixup call sites
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
6.discover protocols
处理协议
// Discover protocols. Fix up protocol refs.
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
7.fix up @protocol references
修正协议引用
// Fix up @protocol references
// Preoptimized images may have the right
// answer already but we don't know for sure.
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
8.Realize non-lazy classes (for +load methods and static instances)
实现非懒加载类 (即实现了+load方法)和静态实例
realizeClass
Performs first-time initialization on class cls,
including allocating its read-write data.
Returns the real class structure for the class.
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// hack for class __ARCLite__, which didn't get this above
#if TARGET_OS_SIMULATOR
if (cls->cache._buckets == (void*)&_objc_empty_cache &&
(cls->cache._mask || cls->cache._occupied))
{
cls->cache._mask = 0;
cls->cache._occupied = 0;
}
if (cls->ISA()->cache._buckets == (void*)&_objc_empty_cache &&
(cls->ISA()->cache._mask || cls->ISA()->cache._occupied))
{
cls->ISA()->cache._mask = 0;
cls->ISA()->cache._occupied = 0;
}
#endif
addClassTableEntry(cls);
realizeClass(cls);
}
}
9.Realize newly-resolved future classes, in case CF manipulates them
// Realize newly-resolved future classes, in case CF manipulates them
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
realizeClass(resolvedFutureClasses[i]);
resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
10.Discover categories.
处理分类,这里会将分类的方法/属性/协议添加到类的rw中(remethodizeClass->attachCategories)
// Discover categories.
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);
if (!cls) {
// Category's target class is missing (probably weak-linked).
// Disavow any knowledge of this category.
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
// 这里会将分类的方法/属性/协议添加到类的rw中
// Process this category.
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
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);
}
}
}
}
load_images
调用时机:
作为_dyld_objc_notify_register的init回调:注册的时候会对所有已初始化的镜像立即进行调用;另外会在镜像初始化时调用;在dyld源码搜索(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
主要流程:
准备和调用+load方法:prepare_load_methods和call_load_methods
prepare_load_methods
- 对非懒加载的类(实现了+load方法的类)进行处理
- 对非懒加载的分类(实现了+load方法的分类)进行处理
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
// 1. 对非懒加载类进行处理
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
// 2. 对非懒加载的分类进行处理
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);
}
}
- 确保父类优先
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);
}
- 最终实现了+load方法的类和分类会放入结构数组loadable_classes和loadable_categories中
自己看代码吧,很简单
call_load_methods
调用+load方法。
重点
调用顺序:父类>子类>分类。(但是如果+load方法调用时,有关联的镜像被映射时,可能会有变化?存疑)
+load方法只调用一次
+load方法是通过函数指针直接调用的,不是通过消息发送机制。
补充:_mod_init_func即C++ attribute constructor在+load后面调用,源码在dyld里面
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();
/**
两层循环, 确保不会在+load方法的时候, 有更多可执行+load的方法的Class或Category被触发了.
*/
do {
// 调用Class的+load方法
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 调用Category的+load方法
// 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);
objc_autoreleasePoolPop(pool);
loading = NO;
}
网友评论