类的加载

作者: 只写Bug程序猿 | 来源:发表于2020-01-13 17:54 被阅读0次

    类的加载

    1.0 objc_init分析

    通过对dyld动态链接流程的分析最后会来到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);
    }
    
    1.0.1 environ_init 初始化环境变量
    void environ_init(void) 
    {
            ...
            //代码太长省略
            for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
                const option_t *opt = &Settings[i];            
                if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
                if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
            }
            ...
    }
    

    这里是经过循环打印所有的环境变量,我们直接拷贝这段代码,到判断条件外边.直接打印下环境变量

    objc[4791]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
    objc[4791]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
    objc[4791]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
    objc[4791]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
    objc[4791]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
    objc[4791]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
    objc[4791]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
    objc[4791]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
    objc[4791]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
    objc[4791]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
    objc[4791]: OBJC_PRINT_CACHE_SETUP: log processing of method caches
    objc[4791]: OBJC_PRINT_FUTURE_CLASSES: log use of future classes for toll-free bridging
    objc[4791]: OBJC_PRINT_PREOPTIMIZATION: log preoptimization courtesy of dyld shared cache
    objc[4791]: OBJC_PRINT_CXX_CTORS: log calls to C++ ctors and dtors for instance variables
    objc[4791]: OBJC_PRINT_EXCEPTIONS: log exception handling
    objc[4791]: OBJC_PRINT_EXCEPTION_THROW: log backtrace of every objc_exception_throw()
    objc[4791]: OBJC_PRINT_ALT_HANDLERS: log processing of exception alt handlers
    objc[4791]: OBJC_PRINT_REPLACED_METHODS: log methods replaced by category implementations
    objc[4791]: OBJC_PRINT_DEPRECATION_WARNINGS: warn about calls to deprecated runtime functions
    objc[4791]: OBJC_PRINT_POOL_HIGHWATER: log high-water marks for autorelease pools
    objc[4791]: OBJC_PRINT_CUSTOM_RR: log classes with un-optimized custom retain/release methods
    objc[4791]: OBJC_PRINT_CUSTOM_AWZ: log classes with un-optimized custom allocWithZone methods
    objc[4791]: OBJC_PRINT_RAW_ISA: log classes that require raw pointer isa fields
    objc[4791]: OBJC_DEBUG_UNLOAD: warn about poorly-behaving bundles when unloaded
    objc[4791]: OBJC_DEBUG_FRAGILE_SUPERCLASSES: warn about subclasses that may have been broken by subsequent changes to superclasses
    objc[4791]: OBJC_DEBUG_NIL_SYNC: warn about @synchronized(nil), which does no synchronization
    objc[4791]: OBJC_DEBUG_NONFRAGILE_IVARS: capriciously rearrange non-fragile ivars
    objc[4791]: OBJC_DEBUG_ALT_HANDLERS: record more info about bad alt handler use
    objc[4791]: OBJC_DEBUG_MISSING_POOLS: warn about autorelease with no pool in place, which may be a leak
    objc[4791]: OBJC_DEBUG_POOL_ALLOCATION: halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools
    objc[4791]: OBJC_DEBUG_DUPLICATE_CLASSES: halt when multiple classes with the same name are present
    objc[4791]: OBJC_DEBUG_DONT_CRASH: halt the process by exiting instead of crashing
    objc[4791]: OBJC_DISABLE_VTABLES: disable vtable dispatch
    objc[4791]: OBJC_DISABLE_PREOPTIMIZATION: disable preoptimization courtesy of dyld shared cache
    objc[4791]: OBJC_DISABLE_TAGGED_POINTERS: disable tagged pointer optimization of NSNumber et al.
    objc[4791]: OBJC_DISABLE_TAG_OBFUSCATION: disable obfuscation of tagged pointers
    objc[4791]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields
    objc[4791]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
    

    可能好多码友这样干过,比如在Edit Scheme 里边添加环境变量如图

    环境变量

    看 一个例子

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            LGPerson *object = [LGPerson alloc];
            NSLog(@"Hello, World! %@",object);
    //        [object saySomething];
        }
        return 0;
    }
    

    进行lldb打印

    (lldb) x/4xg object
    0x101230830: 0x001d800100001191 0x0000000000000000
    0x101230840: 0x646e6946534e5b2d 0x536e726574746150
    (lldb) p/x LGPerson.class
    (Class) $9 = 0x0000000100001190 LGPerson
    (lldb) p/x 0x001d800100001191 & 0x00007ffffffffff8
    (long) $10 = 0x0000000100001190
    (lldb) 
    

    打印Person类,地址为0x0000000100001190
    拿到isa:0x001d800100001191mask:0x00007ffffffffff8进行与运算 得出 0x0000000100001190
    我们发现是一样的,因为现在的isa指针是没有优化的,所以要进行&运算
    那么现在加上OBJC_DISABLE_NONPOINTER_ISA这个环境变量并设置为YES,表示对isa进行优化,再来看

     (lldb) p/x LGPerson.class
    (Class) $0 = 0x0000000100001190 LGPerson
    (lldb) x/4xg object
    0x101636c70: 0x0000000100001190 0x0000000000000000
    0x101636c80: 0x72626956534e5b2d 0x74696c7053746e61
    

    不用进行&运算直接就是相等的
    在来看一个例子OBJC_PRINT_LOAD_METHODS环境变量

    OBJC_PRINT_LOAD_METHODS
    
    objc[5000]: LOAD: class '__IncompleteProtocol' scheduled for +load
    objc[5000]: LOAD: class 'Protocol' scheduled for +load
    objc[5000]: LOAD: class '__NSUnrecognizedTaggedPointer' scheduled for +load
    objc[5000]: LOAD: +[__IncompleteProtocol load]
    
    objc[5000]: LOAD: +[Protocol load]
    
    objc[5000]: LOAD: +[__NSUnrecognizedTaggedPointer load]
    
    objc[5000]: LOAD: category 'NSObject(NSObject)' scheduled for +load
    objc[5000]: LOAD: +[NSObject(NSObject) load]
    
    objc[5000]: LOAD: category 'NSObject(NSObject)' scheduled for +load
    objc[5000]: LOAD: +[NSObject(NSObject) load]
    
    objc[5000]: LOAD: category 'CIFilter(Interposer)' scheduled for +load
    objc[5000]: LOAD: +[CIFilter(Interposer) load]
    
    objc[5000]: LOAD: class 'NSApplication' scheduled for +load
    objc[5000]: LOAD: class 'NSBinder' scheduled for +load
    objc[5000]: LOAD: class 'NSColorSpaceColor' scheduled for +load
    objc[5000]: LOAD: class 'NSNextStepFrame' scheduled for +load
    objc[5000]: LOAD: category 'NSColor(NSUIKitSupport)' scheduled for +load
    objc[5000]: LOAD: +[NSApplication load]
    
    objc[5000]: LOAD: +[NSBinder load]
    
    objc[5000]: LOAD: +[NSColorSpaceColor load]
    
    objc[5000]: LOAD: +[NSNextStepFrame load]
    
    objc[5000]: LOAD: +[NSColor(NSUIKitSupport) load]
    
    objc[5000]: LOAD: category 'NSError(FPAdditions)' scheduled for +load
    objc[5000]: LOAD: +[NSError(FPAdditions) load]
    
    objc[5000]: LOAD: class '_DKEventQuery' scheduled for +load
    objc[5000]: LOAD: +[_DKEventQuery load]
    
    objc[5000]: LOAD: class 'LGPerson' scheduled for +load
    objc[5000]: LOAD: +[LGPerson load]
    

    将所有实现的load方法打印出来.load方法太多将影响启动速度,利用这个环境变量可以对load方法进行查看
    问题: 我没有源码怎么办
    那么来一个装逼的命令export OBJC_HELP=1
    cd到当前项目根目录,然后终端输入该命令然后回车键,可能有些人输入之后没有反应,可以在随便输入一个简单的命令比如ls,然后回车OK,完美

    1.0.2 tls_init()
    void tls_init(void)
    {
    #if SUPPORT_DIRECT_THREAD_KEYS
        _objc_pthread_key = TLS_DIRECT_KEY;
        pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
    #else
        _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
    #endif
    }
    

    主要是线程的key的绑定

    1.0.3 static_init()
    /***********************************************************************
    * static_init
    * Run C++ static constructor functions.
    * libc calls _objc_init() before dyld would call our static constructors, 
    * so we have to do it ourselves.
    **********************************************************************/
    static void static_init()
    {
        size_t count;
        auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
        for (size_t i = 0; i < count; i++) {
            inits[i]();
        }
    }
    

    注释告诉我们,这里会调用C++的静态构造函数

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            LGPerson *object = [LGPerson alloc];
            NSLog(@"Hello, World! %@",object);
    //        [object saySomething];
        }
        return 0;
    }
    

    在inits打断点,然后运行项目,发现count现在为11.我们现在构造几个C++函数,再次运行

    struct LGTeacher{
        LGTeacher(){
            printf("LGTeacher 初始化 \n");
        }
        ~LGTeacher(){
            printf("LGTeacher 析构了");
        }
    };
    
    struct LGFunc{
        LGFunc(){
            printf("LGFunc 初始化 \n");
        }
        ~LGFunc(){
            printf("LGFunc 析构了");
        }
    };
    

    发现count还是11.显然,这里不是调用我们自己实现的C++构造函数,而是系统的一些C++.

    • libc calls _objc_init() before dyld would call our static constructors, 这句注释也告诉我们原因,_objc_init方法在dyld调用我们自己的静态方法之前.
    1.0.4 lock_init
    void lock_init(void)
    {
    }
    

    啥也没有,可能是让我们重写,也可能是预留接口,也可能是没有开源.在这装逼,苹果这一天天的.

    1.0.5 exception_init

    异常初始化.监听回调异常,

    static void _objc_terminate(void)
    {
        if (PrintExceptions) {
            _objc_inform("EXCEPTIONS: terminating");
        }
    
        if (! __cxa_current_exception_type()) {
            // No current exception.
            (*old_terminate)();
        }
        else {
            // There is a current exception. Check if it's an objc exception.
            @try {
                __cxa_rethrow();
            } @catch (id e) {
                // It's an objc object. Call Foundation's handler, if any.
                (*uncaught_handler)((id)e);
                (*old_terminate)();
            } @catch (...) {
                // It's not an objc object. Continue to C++ terminate.
                (*old_terminate)();
            }
        }
    }
    

    只要有异常,就会进入这里

    1.0.6 map_images
    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[])
    {
       //...省略代码,这些代码都是一些计算hCount,以及一些打印
        if (hCount > 0) {
    //读取镜像,这里是重要代码
            _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
        }
    
        firstTime = NO;
    }
    
    void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
    {
    //简化后的代码
    // 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);
           }
       }
    }
    
    // 2:类处理
        for (i = 0; i < count; i++) {
          Class cls = (Class)classlist[i];
          Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
        }
    

    此时cls还不是名字,只是一个地址,因为还没有处理,还没有读取所以还读不出类只是一个类的一个地址.

    1.0.6.1 readClass
    • readClass做了些什么呢
    Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized){
    
    if (Class newCls = popFutureNamedClass(mangledName)) {
           // This name was previously allocated as a future class.
           // Copy objc_class to future class's struct.
           // Preserve future's rw data block.
           
           if (newCls->isAnySwift()) {
               _objc_fatal("Can't complete future class request for '%s' "
                           "because the real class is too big.", 
                           cls->nameForLogging());
           }
           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;
       }
         addNamedClass(cls, mangledName, replacing);
          addClassTableEntry(cls);
    }
    

    有些人说这里是对rw,ro等一系列进行处理,其实不是的,因为这里打个断点,根本是不进来的.这里最重要的是下边两个函数

    static void addNamedClass(Class cls, const char *name, Class replacing = nil)
    {
        runtimeLock.assertLocked();
        Class old;
        if ((old = getClassExceptSomeSwift(name))  &&  old != replacing) {
            inform_duplicate(name, old, cls);
    
            // getMaybeUnrealizedNonMetaClass uses name lookups.
            // Classes not found by name lookup must be in the
            // secondary meta->nonmeta table.
            addNonMetaClass(cls);
        } else {
            NXMapInsert(gdb_objc_realized_classes, name, cls);
        }
        assert(!(cls->data()->flags & RO_META));
    
        // wrong: constructed classes are already realized when they get here
        // assert(!cls->isRealized());
    }
    

    将读取到的类加入到gdb_objc_realized_classes总表中

    static void addClassTableEntry(Class cls, bool addMeta = true) {
        runtimeLock.assertLocked();
    
        // This class is allowed to be a known class via the shared cache or via
        // data segments, but it is not allowed to be in the dynamic table already.
        assert(!NXHashMember(allocatedClasses, cls));
    
        if (!isKnownClass(cls))
            NXHashInsert(allocatedClasses, cls);
        if (addMeta)
            addClassTableEntry(cls->ISA(), false);
    }
    

    将读取到的类和元类加入到allocatedClasses,因为这个时候已经分配到了内存

    // Realize non-lazy classes (for +load methods and static instances)
        // 实现非懒加载的类,对于load方法和静态实例变量
        for (EACH_HEADER) {
            classref_t *classlist = 
                _getObjc2NonlazyClassList(hi, &count);
            for (i = 0; i < count; i++) {
                Class cls = remapClass(classlist[i]);
                // printf("non-lazy Class:%s\n",cls->mangledName());
                if (!cls) continue;
                addClassTableEntry(cls);
                if (cls->isSwiftStable()) {
                    if (cls->swiftMetadataInitializer()) {
                        _objc_fatal("Swift class %s with a metadata initializer "
                                    "is not allowed to be non-lazy",
                                    cls->nameForLogging());
                    }
                    // fixme also disallow relocatable classes
                    // We can't disallow all Swift classes because of
                    // classes like Swift.__EmptyArrayStorage
                }
                // 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
                realizeClassWithoutSwift(cls);
            }
        }
    
    1.0.6.2 realizeClassWithoutSwift

    realizeClassWithoutSwift开始对类进行处理

    static Class realizeClassWithoutSwift(Class cls)
    {
         if (!cls) return nil;
        if (cls->isRealized()) return cls;
        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);
        }
         supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
         cls->superclass = supercls;
        cls->initClassIsa(metacls);
        if (supercls) {
            addSubclass(supercls, cls);
        } else {
            addRootClass(cls);
        }
     // Attach categories
        methodizeClass(cls);
    }
    

    先看一下else条件里边,对rw的ro进行赋值,这时候rw还没有进行处理,所以rw里的methods,propertys等属性还没有值.
    继续看

    // Update superclass and metaclass in case of remapping
       cls->superclass = supercls;
       cls->initClassIsa(metacls);
    

    为了保证superClass和isa的完整性,递归对父类和元类进行处理,而递归的出口就是if (!cls) return nil;找父类最终会找到NSObject,而NSObject的父类就是nil.然后你将值复制给class

     if (supercls) {
            addSubclass(supercls, cls);
        }
    

    然后判断是否为父类,如果是执行addSubclass,完善继承链接关系

    1.0.6.3 methodizeClass

    然后调用methodizeClass,对rw进行处理

    static void methodizeClass(Class cls)
    {
    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);
        }
    
        // Root classes get bonus method implementations if they don't have 
        // them already. These apply before category replacements.
        if (cls->isRootMetaclass()) {
            // root metaclass
            addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
        }
     category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
        attachCategories(cls, cats, false /*don't flush caches*/);
    
    }
    

    取到ro.然后将baseMethod,properties,protolist,以及分类等等一系列贴到rw里边去,为什么要存两份呢
    ro : readonly
    rw : read write
    从名字就可以看出一个可读可写,一个只读,目的就是为了防止被玩坏,所以另存一份.

    void attachLists(List* const * addedLists, uint32_t addedCount) {
            if (addedCount == 0) return;
    
            if (hasArray()) {
                // 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]));
            }
            else if (!list  &&  addedCount == 1) {
                // 0 lists -> 1 list
                list = addedLists[0];
            } 
            else {
                // 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]));
            }
        }
    

    分为多对多,一对多,一对一等几种情况
    多对多: 拿到oldCountaddedCount ,进行扩容,然后将源list移到新的list的末尾,然后将新的list拷贝到前边
    一对一: 直接在加到源list的首尾
    一对多 : 先扩容然后将新的list 拷贝到前边

    总结:
    • _read_images ,
      1.0 创建表,
      1.1 读取所有类
      1.2 获取所有类的引用
      1.3 处理方法编号
      1.4 修复旧的objc_msgSend_fixup
      1.5 处理协议
      1.6 重映射协议的引用
      1.7 实现非懒加载类
      1.8 处理分类
    • readClass :将类插入到表中
    • realizeClassWithoutSwift :对类的ro,以及父类等进行处理
    • methodizeClass : 对rw进行处理

    相关文章

      网友评论

        本文标题:类的加载

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