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. 如果分类有的方法,原来没有,那么会把分类的方法放到后面,为什么这样做:

 

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

 

原文地址:https://www.cnblogs.com/coolcold/p/12047997.html