zoukankan      html  css  js  c++  java
  • Categary 分类实现原理

    1. 分类和类定义相同的方法,会不会覆盖类方法:

     

    #import "FXPerson.h"

     

    @implementation FXPerson

    - (void)walk{

        NSLog(@"person walk");

    }

    @end

     

    #import "FXPerson+something.h"

     

    @implementation FXPerson (something)

    -(void)walk{

        NSLog(@"category walk");

    }

    @end

     

    只调用了分类的方法,表面上看是覆盖了类方法,打印所有类方法

    #pragma mark - 遍历方法

    - (void)printMethodNamesOfClass:(Class)cls

    {

        unsigned int count;

        // 获得方法数组

        Method *methodList = class_copyMethodList(cls, &count);

        // 存储方法名

        NSMutableString *methodNames = [NSMutableString string];

        // 遍历所有的方法

        for (int i = 0; i < count; i++) {

            // 获得方法

            Method method = methodList[i];

            // 获得方法名

            NSString *methodName = NSStringFromSelector(method_getName(method));

            // 拼接方法名

            [methodNames appendString:methodName];

            [methodNames appendString:@", "];

        }

        // 释放

        free(methodList);

        // 打印方法名

        NSLog(@"%@ - %@", cls, methodNames);

    }

    打印了两个walk,所以并没有覆盖原来类的walk,这是后需要看源码来拨开云雾见青天,搜索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);

    }

    init方法里面包含所有的镜像加载,在我们的APP包里面有一个可执行文件,使用MachOView软件可以查看镜像

     

     

    查看镜像如何加载 

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

    }

     

    void 

    map_images_nolock(unsigned mhCount, const char * const mhPaths[],

                      const struct mach_header * const mhdrs[])

    {

        static bool firstTime = YES;

        header_info *hList[mhCount];

        uint32_t hCount;

        size_t selrefCount = 0;

     

        // Perform first-time initialization if necessary.

        // This function is called before ordinary library initializers. 

        // fixme defer initialization until an objc-using image is found?

        if (firstTime) {

            preopt_init();

        }

     

        if (PrintImages) {

            _objc_inform("IMAGES: processing %u newly-mapped images... ", mhCount);

        }

     

     

        // Find all images with Objective-C metadata.

        hCount = 0;

     

        // Count classes. Size various table based on the total.

        int totalClasses = 0;

        int unoptimizedTotalClasses = 0;

        {

            uint32_t i = mhCount;

            while (i--) {

                const headerType *mhdr = (const headerType *)mhdrs[i];

     

                auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);

                if (!hi) {

                    // no objc data in this entry

                    continue;

                }

                

                if (mhdr->filetype == MH_EXECUTE) {

                    // Size some data structures based on main executable's size

    #if __OBJC2__

                    size_t count;

                    _getObjc2SelectorRefs(hi, &count);

                    selrefCount += count;

                    _getObjc2MessageRefs(hi, &count);

                    selrefCount += count;

    #else

                    _getObjcSelectorRefs(hi, &selrefCount);

    #endif

                    

    #if SUPPORT_GC_COMPAT

                    // Halt if this is a GC app.

                    if (shouldRejectGCApp(hi)) {

                        _objc_fatal_with_reason

                            (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, 

                             OS_REASON_FLAG_CONSISTENT_FAILURE, 

                             "Objective-C garbage collection " 

                             "is no longer supported.");

                    }

    #endif

                }

                

                hList[hCount++] = hi;

                

                if (PrintImages) {

                    _objc_inform("IMAGES: loading image for %s%s%s%s%s ", 

                                 hi->fname(),

                                 mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",

                                 hi->info()->isReplacement() ? " (replacement)" : "",

                                 hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "",

                                 hi->info()->optimizedByDyld()?" (preoptimized)":"");

                }

            }

        }

     

        // Perform one-time runtime initialization that must be deferred until 

        // the executable itself is found. This needs to be done before 

        // further initialization.

        // (The executable may not be present in this infoList if the 

        // executable does not contain Objective-C code but Objective-C 

        // is dynamically loaded later.

        if (firstTime) {

            sel_init(selrefCount);

            arr_init();

     

    #if SUPPORT_GC_COMPAT

            // Reject any GC images linked to the main executable.

            // We already rejected the app itself above.

            // Images loaded after launch will be rejected by dyld.

     

            for (uint32_t i = 0; i < hCount; i++) {

                auto hi = hList[i];

                auto mh = hi->mhdr();

                if (mh->filetype != MH_EXECUTE  &&  shouldRejectGCImage(mh)) {

                    _objc_fatal_with_reason

                        (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, 

                         OS_REASON_FLAG_CONSISTENT_FAILURE, 

                         "%s requires Objective-C garbage collection "

                         "which is no longer supported.", hi->fname());

                }

            }

    #endif

     

    #if TARGET_OS_OSX

            // Disable +initialize fork safety if the app is too old (< 10.13).

            // Disable +initialize fork safety if the app has a

            //   __DATA,__objc_fork_ok section.

     

            if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_13) {

                DisableInitializeForkSafety = true;

                if (PrintInitializing) {

                    _objc_inform("INITIALIZE: disabling +initialize fork "

                                 "safety enforcement because the app is "

                                 "too old (SDK version " SDK_FORMAT ")",

                                 FORMAT_SDK(dyld_get_program_sdk_version()));

                }

            }

     

            for (uint32_t i = 0; i < hCount; i++) {

                auto hi = hList[i];

                auto mh = hi->mhdr();

                if (mh->filetype != MH_EXECUTE) continue;

                unsigned long size;

                if (getsectiondata(hi->mhdr(), "__DATA", "__objc_fork_ok", &size)) {

                    DisableInitializeForkSafety = true;

                    if (PrintInitializing) {

                        _objc_inform("INITIALIZE: disabling +initialize fork "

                                     "safety enforcement because the app has "

                                     "a __DATA,__objc_fork_ok section");

                    }

                }

                break;  // assume only one MH_EXECUTE image

            }

    #endif

     

        }

     

        if (hCount > 0) {

            _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);

        }

     

        firstTime = NO;

    }

     

    进入 _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);

     

    void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)

     

    {

     

        header_info *hi;

     

        uint32_t hIndex;

     

        size_t count;

     

        size_t i;

     

        Class *resolvedFutureClasses = nil;

     

        size_t resolvedFutureClassCount = 0;

     

        static bool doneOnce;

     

        TimeLogger ts(PrintImageTimes);

     

     

     

        runtimeLock.assertLocked();

     

     

     

    #define EACH_HEADER

     

        hIndex = 0;        

     

        hIndex < hCount && (hi = hList[hIndex]);

     

        hIndex++

     

     

     

        if (!doneOnce) {

     

            doneOnce = YES;

     

     

     

    #if SUPPORT_NONPOINTER_ISA

     

            // Disable non-pointer isa under some conditions.

     

     

     

    # if SUPPORT_INDEXED_ISA

     

            // Disable nonpointer isa if any image contains old Swift code

     

            for (EACH_HEADER) {

     

                if (hi->info()->containsSwift()  &&

     

                    hi->info()->swiftVersion() < objc_image_info::SwiftVersion3)

     

                {

     

                    DisableNonpointerIsa = true;

     

                    if (PrintRawIsa) {

     

                        _objc_inform("RAW ISA: disabling non-pointer isa because "

     

                                     "the app or a framework contains Swift code "

     

                                     "older than Swift 3.0");

     

                    }

     

                    break;

     

                }

     

            }

     

    # endif

     

     

     

    # if TARGET_OS_OSX

     

            // Disable non-pointer isa if the app is too old

     

            // (linked before OS X 10.11)

     

            if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {

     

                DisableNonpointerIsa = true;

     

                if (PrintRawIsa) {

     

                    _objc_inform("RAW ISA: disabling non-pointer isa because "

     

                                 "the app is too old (SDK version " SDK_FORMAT ")",

     

                                 FORMAT_SDK(dyld_get_program_sdk_version()));

     

                }

     

            }

     

     

     

            // Disable non-pointer isa if the app has a __DATA,__objc_rawisa section

     

            // New apps that load old extensions may need this.

     

            for (EACH_HEADER) {

     

                if (hi->mhdr()->filetype != MH_EXECUTE) continue;

     

                unsigned long size;

     

                if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {

     

                    DisableNonpointerIsa = true;

     

                    if (PrintRawIsa) {

     

                        _objc_inform("RAW ISA: disabling non-pointer isa because "

     

                                     "the app has a __DATA,__objc_rawisa section");

     

                    }

     

                }

     

                break;  // assume only one MH_EXECUTE image

     

            }

     

    # endif

     

     

     

    #endif

     

     

     

            if (DisableTaggedPointers) {

     

                disableTaggedPointers();

     

            }

     

            

     

            initializeTaggedPointerObfuscator();

     

     

     

            if (PrintConnecting) {

     

                _objc_inform("CLASS: found %d classes during launch", totalClasses);

     

            }

     

     

     

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

     

            

     

            ts.log("IMAGE TIMES: first time tasks");

     

        }

     

     

     

     

     

        // Discover classes. Fix up unresolved future classes. Mark bundle classes.

     

     

     

        for (EACH_HEADER) {

     

            classref_t *classlist = _getObjc2ClassList(hi, &count);

     

            

     

            if (! mustReadClasses(hi)) {

     

                // Image is sufficiently optimized that we need not call readClass()

     

                continue;

     

            }

     

     

     

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

     

     

     

        // 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]);

     

                }

     

            }

     

        }

     

     

     

        ts.log("IMAGE TIMES: remap classes");

     

     

     

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

     

                }

     

            }

     

        }

     

     

     

        ts.log("IMAGE TIMES: fix up selector references");

     

     

     

    #if SUPPORT_FIXUP

     

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

     

            }

     

        }

     

     

     

        ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");

     

    #endif

     

     

     

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

     

            }

     

        }

     

     

     

        ts.log("IMAGE TIMES: discover protocols");

     

     

     

        // 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]);

     

            }

     

        }

     

     

     

        ts.log("IMAGE TIMES: fix up @protocol references");

     

     

     

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

     

            }

     

        }

     

     

     

        ts.log("IMAGE TIMES: realize non-lazy classes");

     

     

     

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

     

        }    

     

     

     

        ts.log("IMAGE TIMES: realize future classes");

     

     

     

        // 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;

     

                }

     

     

     

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

     

                    }

     

                }

     

            }

     

        }

     

     

     

        ts.log("IMAGE TIMES: discover categories");

     

     

     

        // Category discovery MUST BE LAST to avoid potential races 

     

        // when other threads call the new category code before 

     

        // this thread finishes its fixups.

     

     

     

        // +load handled by prepare_load_methods()

     

     

     

        if (DebugNonFragileIvars) {

     

            realizeAllClasses();

     

        }

     

     

     

     

     

        // Print preoptimization statistics

     

        if (PrintPreopt) {

     

            static unsigned int PreoptTotalMethodLists;

     

            static unsigned int PreoptOptimizedMethodLists;

     

            static unsigned int PreoptTotalClasses;

     

            static unsigned int PreoptOptimizedClasses;

     

     

     

            for (EACH_HEADER) {

     

                if (hi->isPreoptimized()) {

     

                    _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors "

     

                                 "in %s", hi->fname());

     

                }

     

                else if (hi->info()->optimizedByDyld()) {

     

                    _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors "

     

                                 "in %s", hi->fname());

     

                }

     

     

     

                classref_t *classlist = _getObjc2ClassList(hi, &count);

     

                for (i = 0; i < count; i++) {

     

                    Class cls = remapClass(classlist[i]);

     

                    if (!cls) continue;

     

     

     

                    PreoptTotalClasses++;

     

                    if (hi->isPreoptimized()) {

     

                        PreoptOptimizedClasses++;

     

                    }

     

                    

     

                    const method_list_t *mlist;

     

                    if ((mlist = ((class_ro_t *)cls->data())->baseMethods())) {

     

                        PreoptTotalMethodLists++;

     

                        if (mlist->isFixedUp()) {

     

                            PreoptOptimizedMethodLists++;

     

                        }

     

                    }

     

                    if ((mlist=((class_ro_t *)cls->ISA()->data())->baseMethods())) {

     

                        PreoptTotalMethodLists++;

     

                        if (mlist->isFixedUp()) {

     

                            PreoptOptimizedMethodLists++;

     

                        }

     

                    }

     

                }

     

            }

     

     

     

            _objc_inform("PREOPTIMIZATION: %zu selector references not "

     

                         "pre-optimized", UnfixedSelectors);

     

            _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) method lists pre-sorted",

     

                         PreoptOptimizedMethodLists, PreoptTotalMethodLists, 

     

                         PreoptTotalMethodLists

     

                         ? 100.0*PreoptOptimizedMethodLists/PreoptTotalMethodLists 

     

                         : 0.0);

     

            _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) classes pre-registered",

     

                         PreoptOptimizedClasses, PreoptTotalClasses, 

     

                         PreoptTotalClasses 

     

                         ? 100.0*PreoptOptimizedClasses/PreoptTotalClasses

     

                         : 0.0);

     

            _objc_inform("PREOPTIMIZATION: %zu protocol references not "

     

                         "pre-optimized", UnfixedProtocolReferences);

     

        }

     

     

     

    #undef EACH_HEADER

     

    }

     

     

     

    加载classList 镜像:

     

    Class cls = (Class)classlist[i];

     

    Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized)

     

    加载方法编号镜像, 加载fixobjc_msgSend_fixup镜像(一些修复信息),加载协议镜像,加载分类镜像,把这些都加载到缓存表中

     

    加载分类镜像:

     

    remethodizeClass(cls->ISA());

     

    static void remethodizeClass(Class cls)

    {

        category_list *cats;

        bool isMeta;

     

        runtimeLock.assertLocked();

     

        isMeta = cls->isMetaClass();

     

        // Re-methodizing: check for more categories

        if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {

            if (PrintConnecting) {

                _objc_inform("CLASS: attaching categories to class '%s' %s", 

                             cls->nameForLogging(), isMeta ? "(meta)" : "");

            }

            

            attachCategories(cls, cats, true /*flush caches*/);        

            free(cats);

        }

    }

     

    把cats添加到类里面:

    attachCategories(cls, cats, true /*flush caches*/):

     

    // Attach method lists and properties and protocols from categories to a class.

    // Assumes the categories in cats are all loaded and sorted by load order, 

    // oldest categories first.

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

    }

     

    首先准备三张表:

     

    method_list_t(方法), property_list_t(属性), protocol_list_t(协议)

     

    然后使用rw->methods.attachLists添加进入cls的相应三张表中

     

    此时类的加载机制:

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

            }

        }

     

    进入realizeClass(cls):

     

    /***********************************************************************

    * realizeClass

    * Performs first-time initialization on class cls, 

    * including allocating its read-write data.

    * Returns the real class structure for the class. 

    * Locking: runtimeLock must be write-locked by the caller

    **********************************************************************/

    static Class realizeClass(Class cls)

    {

        runtimeLock.assertLocked();

     

        const class_ro_t *ro;

        class_rw_t *rw;

        Class supercls;

        Class metacls;

        bool isMeta;

     

        if (!cls) return nil;

        if (cls->isRealized()) return cls;

        assert(cls == remapClass(cls));

     

        // fixme verify class is not in an un-dlopened part of the shared cache?

     

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

        }

     

        isMeta = ro->flags & RO_META;

     

        rw->version = isMeta ? 7 : 0;  // old runtime went up to 6

     

     

        // Choose an index for this class.

        // Sets cls->instancesRequireRawIsa if indexes no more indexes are available

        cls->chooseClassArrayIndex();

     

        if (PrintConnecting) {

            _objc_inform("CLASS: realizing class '%s'%s %p %p #%u", 

                         cls->nameForLogging(), isMeta ? " (meta)" : "", 

                         (void*)cls, ro, cls->classArrayIndex());

        }

     

        // Realize superclass and metaclass, if they aren't already.

        // This needs to be done after RW_REALIZED is set above, for root classes.

        // This needs to be done after class index is chosen, for root metaclasses.

        supercls = realizeClass(remapClass(cls->superclass));

        metacls = realizeClass(remapClass(cls->ISA()));

     

    #if SUPPORT_NONPOINTER_ISA

        // Disable non-pointer isa for some classes and/or platforms.

        // Set instancesRequireRawIsa.

        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();

        bool rawIsaIsInherited = false;

        static bool hackedDispatch = false;

     

        if (DisableNonpointerIsa) {

            // Non-pointer isa disabled by environment or app SDK version

            instancesRequireRawIsa = true;

        }

        else if (!hackedDispatch  &&  !(ro->flags & RO_META)  &&  

                 0 == strcmp(ro->name, "OS_object")) 

        {

            // hack for libdispatch et al - isa also acts as vtable pointer

            hackedDispatch = true;

            instancesRequireRawIsa = true;

        }

        else if (supercls  &&  supercls->superclass  &&  

                 supercls->instancesRequireRawIsa()) 

        {

            // This is also propagated by addSubclass() 

            // but nonpointer isa setup needs it earlier.

            // Special case: instancesRequireRawIsa does not propagate 

            // from root class to root metaclass

            instancesRequireRawIsa = true;

            rawIsaIsInherited = true;

        }

        

        if (instancesRequireRawIsa) {

            cls->setInstancesRequireRawIsa(rawIsaIsInherited);

        }

    // SUPPORT_NONPOINTER_ISA

    #endif

     

        // Update superclass and metaclass in case of remapping

        cls->superclass = supercls;

        cls->initClassIsa(metacls);

     

        // Reconcile instance variable offsets / layout.

        // This may reallocate class_ro_t, updating our ro variable.

        if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

     

        // Set fastInstanceSize if it wasn't set already.

        cls->setInstanceSize(ro->instanceSize);

     

        // Copy some flags from ro to rw

        if (ro->flags & RO_HAS_CXX_STRUCTORS) {

            cls->setHasCxxDtor();

            if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {

                cls->setHasCxxCtor();

            }

        }

     

        // Connect this class to its superclass's subclass lists

        if (supercls) {

            addSubclass(supercls, cls);

        } else {

            addRootClass(cls);

        }

     

        // Attach categories

        methodizeClass(cls);

     

        return cls;

    }

     

    进入realizeClass(cls):

    可看到吧data()段数据读到了rw中

    rw = cls->data();

    ro = cls->data()->ro;

     

    其中data()就是:

    //Attach categories

    methodizeClass(cls); 

    最终所有的methodList,propertist,Protocollist全部都加载到了rw中

     

    下面看下如何attachLists:

     

    void attachLists(List* const * addedLists, uint32_t addedCount) {

            if (addedCount == 0) return;

     

            if (hasArray()) {

                // many lists -> many lists

                uint32_t oldCount = array()->count;

                uint32_t newCount = oldCount + addedCount;

                setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));

                array()->count = newCount;

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

            }

        }

     

    重点:

     uint32_t oldCount = array()->count;

     uint32_t newCount = oldCount + addedCount;

     setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));

     array()->count = newCount;

     memmove(array()->lists + addedCount, array()->lists, 

     oldCount * sizeof(array()->lists[0]));

     memcpy(array()->lists, addedLists, 

     addedCount * sizeof(array()->lists[0]));简单说下完成的内容:

     

    简单说下完成的内容:

     重新分配array()内存大小为新数组加老数组个数,memmove函数吧老的数据放到array()的从addedCount位置开始到末尾,把addedLists放到了前面从0开始到addedCount。(关于memmove与memcpy请查看我的博客 C语言memmove()函数:复制内存内容(可以处理重叠的内存块))

    所以解释了开始的时候打印了两个walk,并且先打印分类的walk。

     

     

    1. 分类有两个和类相同的方法时,为什么在加载分类的方法列表的时候什么不按顺序依次加载 ?

     

    attach是for循环遍历的中间会添加和删除,而且方法列表是一个哈希表,并不是一个有序的列表,去取值的 时候也是根据LLVM随机取值。只需要保证相同方法名分类的在类的前面

     

    2. 如果分类有的方法,原来没有,那么会把分类的方法放到后面,为什么这样做:

     

    个人认为就是时间空间复杂度的问题,如果吧一个新的变量放到内存前面,那么原来的东西内存地址都要往后面移动,但是如果放到后面,就只需要添加自己,不需要影响任何东西。

     

  • 相关阅读:
    居中
    <script type="text/javascript"></script>
    移动端获取全文高度
    video
    transition animation
    移动端隐藏overflow:auto滚轮
    Vue将组件data内的数据重置
    文字刚刚刚刚刚好的居中
    小程序总结(不断更新)
    vue组件之间的传值
  • 原文地址:https://www.cnblogs.com/coolcold/p/12047997.html
Copyright © 2011-2022 走看看