美文网首页
objc源码阅读

objc源码阅读

作者: 拧发条鸟xds | 来源:发表于2021-09-30 14:17 被阅读0次

参考

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);
    }
}
  1. 确保父类优先
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); 
}
  1. 最终实现了+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;
}

待续

相关文章

网友评论

      本文标题:objc源码阅读

      本文链接:https://www.haomeiwen.com/subject/wesenltx.html