美文网首页
17期_iOS-类的加载

17期_iOS-类的加载

作者: 萧修 | 来源:发表于2023-08-19 01:00 被阅读0次

    懒加载类和非懒加载类

    懒加载类:当需要实现才进行加载的类成为懒加载类,反之称为非懒加载类(无论是否使用到,都会进行加载)

    我们也知道+(void)load方法,会在main()之前调用,自定义的类中实现此方法,此类就会从懒加载类变为非懒加载类

    前面了解到dyld的流程,pre-main()函数,中初始化主程序时,libObjc会在_objc_init()到项目中所有类结构进行初始化。

    libObjc分析

    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();//设置objc的预定义的线程特定键和键的析构函数,来存储objc的私有数据
        static_init();//运行C++静态构造函数
        runtime_init();//
        exception_init();//初始化异常操作系统
    #if __OBJC2__
        cache_t::init();
    #endif
        _imp_implementationWithBlock_init();
    
        //在dyld初始化主程序时,通过指针回调实现images的map,load,unmap操作
        _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    
    #if __OBJC2__
        didCallDyldNotifyRegister = true;
    #endif
    }
    

    map_images

    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);
    }
    
    

    map_images()内部通过map_images_nolock(),此方法内部会对头文件信息遍历,当文件类型为mh_execute时,获取mach-o下的方法段和消息段,为注册方法做准备

    • 注册部分系统方法选择器
    • 自动释放池初始化,全局散列表初始化

    map_images最后函数执行read_imags获取类的初始化信息

    read_images

    获取类的初始化信息,

    在这里面会进行以下处理

    • 类处理
    • 方法编号处理
    • 非懒加载类实现
    • 分类处理
    * Does not perform any Swift-side initialization.
    * Returns the real class structure for the class. 
    * Locking: runtimeLock must be write-locked by the caller
    **********************************************************************/
    static Class realizeClassWithoutSwift(Class cls, Class previously)
    {
        //绝大多数情况都执行Normal class,这里是真正开始设置rw的地方,但是需要注意,rw只赋值了ro和flgs,其他的methods,protocol还未赋值。
        if (ro->flags & RO_FUTURE) {
            // This was a future class. rw data is already allocated.
            rw = cls->data();
            ro = cls->data()->ro();
            ASSERT(!isMeta);
            cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
        } else {
            // Normal class. Allocate writeable class data.
            rw = objc::zalloc<class_rw_t>();
            rw->set_ro(ro);
            rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
            cls->setData(rw);
        }
        
        //递归实现父类和元类,递归出口是cls
        supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
        
         //对类的结构体内部的isa,supercls复制,类的结构体包含isa,supercls,bits,cache_t
        cls->setSuperclass(supercls);
        cls->initClassIsa(metacls);
        
        //如果存在父类,反向将cls添加其子类,否则设置root
        if (supercls) {
            addSubclass(supercls, cls);
        } else {
            addRootClass(cls);
        }
    
    methodizeClass(cls, previously);
    }
    

    methodizeClass

    static void methodizeClass(Class cls, Class previously)
    {
        
        method_list_t *list = ro->baseMethods;
        if (list) {
            prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
            if (rwe) rwe->methods.attachLists(&list, 1);
        }
    
        property_list_t *proplist = ro->baseProperties;
        if (rwe && proplist) {
            rwe->properties.attachLists(&proplist, 1);
        }
    
        protocol_list_t *protolist = ro->baseProtocols;
        if (rwe && protolist) {
            rwe->protocols.attachLists(&protolist, 1);
        }
    }
    

    rw方法列表,属性列表,协议列表

    • 在确定个数且为多个的情况下,旧数据整体向后移动新数据的个数,把新数据插入到列表的前面
    • 在确定个数且为一个情况下,新数据直接插在第一个
    • 不确定个数的情况下,旧数据往后移动一个,新数据插入一个在前面,反复执行到添加结束

    总之:新数据在旧数据前面

    问题

    • 类和分类谁先被加入rw

    先类后分类, 分类的数据是需要到rw中才能生效,先加载类,是其拥有rw。即使有分类先被执行,也是被保存了起来。

    • 有ro之后为何还有rw

    oc是动态的,除了编译期数据,还需要运行时添加

    • ro和rw的区别

    ro存储类在编译期就确定的属性,方法,协议
    rw运行时确定,会将ro的内容拷贝

    • macho里面的数据怎么到内存的

    读取macho对应字段下的内容,用哈希表存储,根据表初始化

    相关文章

      网友评论

          本文标题:17期_iOS-类的加载

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