iOS:Category原理

110 阅读2分钟

Category原理

带着问题出发

  • Category的结构了解吗,说来听听??
  • Category的作用是什么,你在项目中用到那些,为什么要这样做。
  • Category里面的同名方法为什么会覆盖Class里面的方法,原理是什么,
  • Category是如何合并到Class上的,什么时候合并的,合并执行了那些操作,attachCategories 的步骤是怎么样的,了解attachLists吗
  • Category存放在哪里,对应mach-o文件的那个段
  • 为什么Category不能存储成员变量,为什么?
  • Category与Extension的区别,如何去验证
  • load方法与initialize的区别,存在继承关系又是如何?是父类的先执行还是什么,没有继承关系的顺序又是怎么样的??
  • initialize方法内部是如何是如何实现的,调用顺序是怎样的 ,子类不存在initialize方法,父类的initialize方法为什么会多次执行。
  • 为什么要设计Load这个方法???
  • 如何统计Load方法的耗时时间

执行代码

注意Person+Test1Person 的一个分类。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Test1.m -o Person+Test1-arm64.cpp

Person+Test1生成的部分代码示例

_category_t _OBJC_$_CATEGORY_Person_$_Test1结构

static struct _category_t _OBJC_$_CATEGORY_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"Person",
	&OBJC_CLASS_$_Person,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test1,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test1,
	0,
	0,
};

instance method

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[4];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	4,
	{{(struct objc_selector *)"test", "v16@0:8", (void *)_I_Person_Test1_test},
	{(struct objc_selector *)"test1", "v16@0:8", (void *)_I_Person_Test1_test1},
	{(struct objc_selector *)"test2", "v16@0:8", (void *)_I_Person_Test1_test2},
	{(struct objc_selector *)"testProtocolRun", "v16@0:8", (void *)_I_Person_Test1_testProtocolRun}}
};

class method

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	2,
	{{(struct objc_selector *)"load", "v16@0:8", (void *)_C_Person_Test1_load},
	{(struct objc_selector *)"initialize", "v16@0:8", (void *)_C_Person_Test1_initialize}}
};

category_t 结构

struct category_t {
    // 名称
    const char *name;
    // isa
    classref_t cls;
    //对象方法
    struct method_list_t *instanceMethods;
    //类方法
    struct method_list_t *classMethods;
    //协议列表
    struct protocol_list_t *protocols;
    //实例属性列表
    struct property_list_t *instanceProperties;
    //class属性列表
    struct property_list_t *_classProperties;
     // note: 元类中保存类方法
    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }
    // 区分类对象,元类对象
     propertiesForMeta(bool isMeta, struct header_info *hi){
        if (!isMeta) return instanceProperties;
        else if (hi->info()->hasCategoryClassProperties()) return _classProperties;
        else return nil;
       }   
};

方法执行顺序

dyld 
-> libSystem.B.dylib : libSystem_initializer 
-> libdispatch.dylib:libdispatch_init 
-> libdispatch.dylib:_os_object_init 
-> libobjc.A.dylib`:_objc_init()

_objc_init 方法

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
     // note: 处理链接库中的类
       // 注册通知:当一个加载镜像image的状态发生改变,将执行对应的方法。
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

dyld中注册通知

void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{

   //赋值操作
  	sNotifyObjCMapped	= mapped;
	sNotifyObjCInit		= init;
	sNotifyObjCUnmapped = unmapped;
}

需要注意的是map_images,load_images会执行多次。【unmapped没有实践】

app启动流程.png

  • 发送map_images通知
static void notifyBatchPartial(dyld_image_states state....){
	if ((state == dyld_image_state_bound) || (orLater && (dyld_image_state_bound > state))) )
	 {
	    //告知objc执行map_images方法		
		(*sNotifyObjCMapped)(objcImageCount, paths, mhs);
	}
}

  • 发送load_images通知

state == dyld_image_state_dependents_initialized时候

if (state == dyld_image_state_dependents_initialized) {
    *sNotifyObjCInit(image->getRealPath(), image->machHeader());
}
  • 发送 unmap_image通知
	if ( image->getState() >= dyld_image_state_bound ) {
		if ( sNotifyObjCUnmapped !=  NULL && image->notifyObjC() )
			(*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader());
	}

map_images方法

关键方法调用顺序

map_images()
└── map_images_nolock()
    └──read_images()
       └──realizeClass()
            └──methodizeClass()
                └──attachCategories()
                  └── prepareMethodLists()

map_images_nolock()方法

读取mhdrs[mhCount]中全部class的数量。

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;
    hCount = 0;
    // 读取mach-o里面的全部类的totalClasses数量,以及不在共享缓存中的unoptimizedTotalClasses
    int totalClasses = 0;
    int unoptimizedTotalClasses = 0;
    {
        uint32_t i = mhCount;
        while (i--) {
            const headerType *mhdr = (const headerType *)mhdrs[i];
            //读取mach_header[i]中的class全部数量
            auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
            if (!hi) {
                continue;
            }
            hList[hCount++] = hi;
        }
    }
    if (hCount > 0) {
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
}

_read_images()方法

  • NXMapTable *gdb_objc_realized_classes初始化,里面存放[className:class]
  • 修复需要realloc的Class为newCls,将信息存入到NXMapTable *remapped_class_map里面, remapped_class_map不为空,循环遍历修改。
  • 注册所有 selector,内存唯一化,存储到NXMapTable *namedSelectors中。
  • 初始化所有 protocol_t。
  • 处理含有load方法的class,进行realizeClass()方法调用。
  • 为重新分配内存指向的Class,执行realizeClass
  • 记录所有 image 中待处理的 category,在 realizeClass 的时候处理。
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{

   // 1.初始化gdb_objc_realized_classes ,存放key=className:Class
    static bool doneOnce;
    if (!doneOnce) {
        doneOnce = YES;
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
    }
    
    
    // 2. 修复需要reallc的class
    for (EACH_HEADER) {
        //判断是否需要提前要执行readClass()方法
        if (! mustReadClasses(hi)) {
            // Image is sufficiently optimized that we need not call readClass()
            continue;
        }

        bool headerIsBundle = hi->isBundle();
        bool headerIsPreoptimized = hi->isPreoptimized();
         //note: 当前 mach-o 中定义的所有类列表classlist
        classref_t *classlist = _getObjc2ClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = (Class)classlist[i];
            //判断cls是否要转变为newCls,将cls的rw信息拷贝到newCls.rw,newCls.ro中。将信息存入到NXMapTable *remapped_class_map【注意这里还没有合并Category中的方法】
            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
                resolvedFutureClasses = (Class *)
                    realloc(resolvedFutureClasses, 
                            (resolvedFutureClassCount+1) * sizeof(Class));
                //记录要重新分配的class,resolvedFutureClasses是个一维数组
                resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
            }
        }
    }
         
    // 修复需要realloc的Class为新newCls
    // 判断 NXMapTable *remapped_class_map是否为空
    if (!noClassesRemapped()) {
        for (EACH_HEADER) {
            //代码中用到的类引用
            Class *classrefs = _getObjc2ClassRefs(hi, &count);
            for (i = 0; i < count; i++) {
                // 用于处理 CoreFoundation toll-bridging 的类,在标为 future class 后会 realloc,因此需要指向最新的内存
                remapClassRef(&classrefs[i]);
            }
            // fixme why doesn't test future1 catch the absence of this?
            //note: 代码中用到的 superclass 引用
            classrefs = _getObjc2SuperRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[i]);
            }
        }
    }
    
    // 3.注册所有 selector,内存唯一化
    static size_t UnfixedSelectors;
    sel_lock();
    for (EACH_HEADER) {
        if (hi->isPreoptimized()) continue;
        bool isBundle = hi->isBundle();
        //代码中用到的 selector 引用
        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);
        }
    }
    sel_unlock();
    
   
    // 4.初始化所有 protocol_t
    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();
        // note: 当前 mach-o 中定义的所有 protocol 列表
        protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map, 
                         isPreoptimized, isBundle);
        }
    }
    // Fix up @protocol references
    // note: 处理所有 protocol_t *,处理 protocol_t 内存被重新创建的情况,让指针指向最新的内存
    // Preoptimized images may have the right 
    // answer already but we don't know for sure.
    for (EACH_HEADER) {
        //// note: 代码中用到的 protocol 引用
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) {
            //上一步的readProtocol可能修改了isa
            remapProtocolRef(&protolist[i]);
        }
    }
    
      
     // 5.处理含有load方法的class,进行realizeClass()处理。
    for (EACH_HEADER) {
        //note: 包含 +load 方法的类
        classref_t *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            // Future Class
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            realizeClass(cls);
        }
    }     
      

    // Realize newly-resolved future classes, in case CF manipulates them
    // CF库中要处理的future classes,执行realizeClass方法
    // 6.为重新分配内存指向的Class,执行realizeClass
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            realizeClass(resolvedFutureClasses[i]);
            resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }


    // 7. 记录所有 image 中待处理的 category,在 realizeClass 的时候处理。
    for (EACH_HEADER) {
        // note: 当前 mach-o 中定义的所有 category 列表
        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) {
                catlist[i] = nil;
                continue;
            }
               bool classExists = NO;
            if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                addUnattachedCategoryForClass(cat, cls, hi);
            }
            if (cat->classMethods  ||  cat->protocols  
                ||  (hasClassProperties && cat->_classProperties)) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
           }
        }
    }
 }

realizeClass方法

  • 创建rw数据,rw.ro = ro
  • 初始化父类,元类
  • non-pointer 处理
  • 重对齐实例变量偏移地址
  • 设置类之间的树形关系,方便之后遍历
  • 执行methodizeClass方法,将ro里面的method,protocol,property复制一份到rw里面
static Class realizeClass(Class cls)
{


   
    const class_ro_t *ro;
    class_rw_t *rw;
    Class supercls;
    Class metacls;
    bool isMeta;
  

    //1. 处理rw,不存在的重新生成rw
    ro = (const class_ro_t *)cls->data();
    // future class,rw数据已经分配过
    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.
        // note: 为 rw 创建内存
        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;
  
    // 2.初始化父类,元类
    // note: 首先初始化父类
    supercls = realizeClass(remapClass(cls->superclass));
    // note: 首先初始化元类
    metacls = realizeClass(remapClass(cls->ISA()));

#if SUPPORT_NONPOINTER_ISA
    // Disable non-pointer isa for some classes and/or platforms.
    // Set instancesRequireRawIsa.
    // 处理non-pointer ,isa_t共用体:non-pointer 0 表示普通的 isa 指针,1 表示使用优化,存储额外信息
    // 3.处理non-pointer
    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")) 
    {
        hackedDispatch = true;
        instancesRequireRawIsa = true;
    }
    else if (supercls  &&  supercls->superclass  &&  
             supercls->instancesRequireRawIsa()) 
    {
           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);

    // 4. 重对齐实例变量偏移地址
    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
    // 5. 设置类之间的树形关系,方便之后遍历
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }
    // Attach categories
    // 5. 将ro里面的method,protocol,property复制一份到rw里面
    methodizeClass(cls);
    return cls;
}

methodizeClass()方法

  • 将ro中的method_list_t,property_list_t,protocol_list_t拷贝到rw中。

  • 元类对象添加initialize类方法

  • attachCategories

static void methodizeClass(Class cls)
{
    auto rw = cls->data();
    auto ro = rw->ro;
    
    method_list_t *list = ro->baseMethods();
    if (list) {
        // 缓存 selector,对 method list 的 selector 进行内存地址唯一化,按照 selector 的地址进行排序.
        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);
    }

    // Attach categories.
    // note: 添加 category 到类上
    category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
    attachCategories(cls, cats, false /*don't flush caches*/);
    if (cats) free(cats);
}

attachCategories()方法

把 category 信息添加到类的 class_rw_t 上。

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));
        
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count;
    bool fromBundle = NO;
    //注意是逆序添加
    while (i--) {
        // note: 逆序添加,因此在 cats 中后面的 method 会在前面
        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;
        }
    }

    //这里操作的是rw
    auto rw = cls->data();

  //对 method list 的 selector 进行内存地址唯一化,按照 selector 的地址进行排序
    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);
}

prepareMethodLists方法

对 method list 的 selector 进行内存地址唯一化,按照 selector 的地址进行排序。

static void 
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount, 
                   bool baseMethods, bool methodsFromBundle)
{
    for (int i = 0; i < addedCount; i++) {
        method_list_t *mlist = addedLists[i];
        if (!mlist->isFixedUp()) {
            //根据 method_t.name < method_t.name排序
            fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
        }
    }
}

attachLists()方法

将已有的数据移动到后面,新增的addList放到最前面

    void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;
        if (hasArray()) {
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            
            //重新分配内存
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            
             //修改count
            array()->count = newCount;
            
            //先将之前的数数据往后移动
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
                    
            // 将addedLists放到array()最前面的addcount连续的位置
            // C库函数 void *memcpy()从存储区 str2 复制 n 个字节到存储区 str1。
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        } else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
       ......
        }
    }

load_images方法

关键方法调用顺序

load_images()
└── prepare_load_methods()
    └──call_load_methods()

load_images()方法

  • 判断image中的class,category中是否含有load方法
  • 将当前的image中含有load方法的class存储到add_class_to_loadable_list数组中,数组中父类在前,子类在后。Category存储到loadable_categories数组中,顺序由编译顺序确定。
  • 执行add_class_to_loadable_list,loadable_categories中的load方法。
void
load_images(const char *path __unused, const struct mach_header *mh)
{
    
    //处理load方法
    //判断当前image中的class,category是否含有load方法
    if (!hasLoadMethods((const headerType *)mh)) return;

    //1.调用prepare_load_methods方法,
    //  将父类,当前class的load具体实现存储到add_class_to_loadable_list数组中,
    //  将categories中的load具体实现放到loadable_categories数组中.
    // 2. 执行load方法
    
    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        rwlock_writer_t lock2(runtimeLock);
        //add_class_to_loadable_list,loadable_categories
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    //执行add_class_to_loadable_list,loadable_categories中的load方法
    call_load_methods();
}

prepare_load_methods()方法

  • 调用schedule_class_load方法,将当前的image中含有load方法的class存储到add_class_to_loadable_list数组中,数组中父类在前,子类在后。
  • 将Category存储到loadable_categories数组中,顺序由编译顺序确定。
void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;
    runtimeLock.assertWriting();
     // 1.将当前的image中含有load方法的class存储到add_class_to_loadable_list数组中,数组中父类在前,子类在后。
    // 获取包含load方法的类列表,
    // note: 包含 +load 方法的类 "__objc_nlclslist")
    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        //父类在前,子类在后
        schedule_class_load(remapClass(classlist[i]));
    }
    
    //  2.将Category存储到loadable_categories数组中,顺序由编译顺序确定。
   // 包含 +load 方法的 category 列表
    category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        if (!cls) continue;  // category for ignored weak-linked class
        realizeClass(cls);
        // note: +load 之前 realize
        assert(cls->ISA()->isRealized());
        //编译顺序决定
        add_category_to_loadable_list(cat);
    }
}

schedule_class_load方法

递归调用,先父类,在子类。

static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
    // note: 先调用父类的 +load
    schedule_class_load(cls->superclass);
    //添加到一维数组中,[父类在前]
    add_class_to_loadable_list(cls);
    //标记为已经load方法已经读取过
    cls->setInfo(RW_LOADED); 
}
void add_class_to_loadable_list(Class cls)
{
    IMP method;  
    method = cls->getLoadMethod();
    if (!method) return;  
    
    //父类在前,子类在后
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}

add_category_to_loadable_list()方法

void add_category_to_loadable_list(Category cat)
{
    IMP method;
    method = _category_getLoadMethod(cat);
    if (!method) return;
    //顺序由编译先后决定
    loadable_categories[loadable_categories_used].cat = cat;
    loadable_categories[loadable_categories_used].method = method;
    loadable_categories_used++;
}

call_load_methods()方法

  • 先调用类定义中的 +load
  • 再调用 category 中的 +load
void call_load_methods(void)
{
  
      do {
        while (loadable_classes_used > 0)
        {
            // note: 先调用类定义中的 +load
            // loadable_classes[0...i]
            // 先调用父类的load,在调用子类的load方法
            call_class_loads();
        }
        // note: 再调用 category 中的 +load
        more_categories = call_category_loads();
    } while (loadable_classes_used > 0  ||  more_categories);  
    
}

call_class_loads()方法

调用loadable_classes数组中的class中的load方法

static void call_class_loads(void)
{
    int i;
    struct loadable_class *classes = loadable_classes;
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 
          //方法调用
        (*load_method)(cls, SEL_load);
    }

call_category_loads()方法

static bool call_category_loads(void)
{
    int i, shift;
    bool new_categories_added = NO;
    
    // Detach current loadable list.
    struct loadable_category *cats = loadable_categories;
    for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        load_method_t load_method = (load_method_t)cats[i].method;
        Class cls;
        if (!cat) continue;
        cls = _category_getClass(cat);
        if (cls  &&  cls->isLoadable()) {
         //调用方法
            (*load_method)(cls, SEL_load);
            cats[i].cat = nil;
        }
    }
    return new_categories_added;
}

initialize 方法

 IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    //// note: 首次调用方法,执行 +initialize
    if (initialize  &&  !cls->isInitialized()) 
    {
       _class_initialize (_class_getNonMetaClass(cls, inst));
    }

核心方法_class_initialize()

  • 先调用父类的initialize方法
  • 在调用子类的initialize方法
  • 调用callInitialize方法
void _class_initialize(Class cls)
{
  
    Class supercls;
    bool reallyInitialize = NO;
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        // note: 先 initialize 父类
        _class_initialize(supercls);
    }
    //调用当前class的initialize方法
     callInitialize(cls);
 }

callInitialize方法

注意这里是objc_msgSend消息机制,先从当前类缓存中查找,再从方法列表中查找,不存在将通过super在父类中查找,依次查找。

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
}

initialize 与 load区别

调用方式

  • load方法:在call_class_loads call_category_loads 中,直接拿到imp,进行方法调用
  • initialize方法:通过objc_msgSend进行方法查找imp,会存在Category,父类的方法执行的情况。

调用时刻

  • load方法:mian函数执行之前,也可以说是load_images中。
  • initialize方法:cls第一次使用的时候。

调用顺序

  • load方法:先调用父类,在调用子类,最后调用Category中的load方法,不存在被覆盖的情况。
  • initialize方法:先调用父类,在调用子类,通过objc_msgSend进行方法查找,子类不存在,则调用父类的方法,会存在category同名方法覆盖的情况。

解决问题

Category结构

struct category_t {
    // 名称
    const char *name;
    // isa
    classref_t cls;
    //对象方法
    struct method_list_t *instanceMethods;
    //类方法
    struct method_list_t *classMethods;
    //协议列表
    struct protocol_list_t *protocols;
    //实例属性列表
    struct property_list_t *instanceProperties;
    //class属性列表
    struct property_list_t *_classProperties;
     // note: 元类中保存类方法
    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }
    // 区分类对象,元类对象
     propertiesForMeta(bool isMeta, struct header_info *hi){
        if (!isMeta) return instanceProperties;
        else if (hi->info()->hasCategoryClassProperties()) return _classProperties;
        else return nil;
       }   
};

Category中的同名方法覆盖问题

首先我们需要知道如何通过sel找到对应的imp。

  • 1.首先从当前class的缓存中查找。
  • 2.从当前class的方法列表中查找。
  • 3.从当前class的的父类查找,递归调用getMethodNoSuper_nolock从父类中重复1、2、3步。

从当前class的方法列表中查找

getMethodNoSuper_nolock()
└── cls->data()->methods()
    └──search_method_list()

getMethodNoSuper_nolock()方法

  • 循环遍历methods,methods是一个二维数组。
  • 执行search_method_list方法,在一维数组中查找对应的method。一维数组查找又分二分查找和普通查找
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
   
    //循环遍历methods,methods是一个二维数组
    for (auto mlists = cls->data()->methods.beginLists(), 
              end = cls->data()->methods.endLists(); 
         mlists != end;
         ++mlists)
    {
        //一维数组查找
        method_t *m = search_method_list(*mlists, sel);
        if (m) return m;
    }

    return nil;
}

search_method_list() 方法

  • 二分查找
  • 普通查找
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{

      // fastpath:表示条件值为1的可能性很大,即很有可能执行if后面的 renturn语句,
     // slowpath:则是很有可能执行else里面的语句

    int methodListIsFixedUp = mlist->isFixedUp();
    int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
    if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
    
        // note: 在排序后的 method list 中进行二分查找
        return findMethodInSortedMethodList(sel, mlist);
        
    } else {
    
        // note: 在未排序的 method list 中线性查找
        for (auto& meth : *mlist) {
            if (meth.name == sel) return &meth;
        }
    }
    return nil;
}

从上面了的过程我们知道了

  1. 我们需要从二维数组中cls->data()->methods查找目标imp方法。
  2. 循环遍历二维数组cls->data()->methods,在每个 一维数组method_t *m中查找

我们的一维数组method_t *m数据是从attachCategoriesattachLists操作中得到的。 我们可以得到这样的结论:

  • Category里面同名方法会覆盖class内的【注意区分对象方法和类方法】
  • 多个Category存在同名方法的,与编译顺序相关,后编译的先执行。【注意是逆序】

Category是如何合并到Class上的

这个问题我觉得可以从_read_images()realizeClass()attachCategories()这个三个方法的内部实现去解释。attachCategories()中的逆序是关键。需要记得要先进行realizeClass()才能执行attachCategories()操作。

Category存放在哪里,对应mach-o文件的那个段

这个问题自己能力有限,解释不清楚,后续会补充完整。

利用clang生成的代码如下。

static struct _category_t _OBJC_$_CATEGORY_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"Person",
	0, // &OBJC_CLASS_$_Person,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test1,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test1,
	0,
	0,
};

为什么Category不能存储成员变量

我们从两个方面去回答:

  • category_t内部结构不存在ivars容器去存放。
  • ivars在编译期间就已经确定,在执行完realizeClass()后,ro的实例变量内存偏移对齐已经完成,且ro的权限为已读。

Category与Extension的区别,如何去验证

假如我们有这样的代码

Class

@interface Person : NSObject

@property (nonatomic,assign) NSInteger age;

@end

Extension

@interface Person ()

@property (nonatomic,assign) NSInteger height;

@end

Category

@interface Person (Test1)

@property (nonatomic,assign) NSInteger weight;

@end

执行下面的代码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person.m -o Person11-arm64.cpp
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Test1.m -o Person11+Test1-arm64.cpp

ivars列表中包含了Extension中的height。说明Extension是Class的一部分。【可以编写多个方法,属性去验证】

static struct /*_ivar_list_t*/ {
	unsigned int entsize;  
	unsigned int count;
	struct _ivar_t ivar_list[2];
} _OBJC_$_INSTANCE_VARIABLES_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_ivar_t),
	2,
	{{(unsigned long int *)&OBJC_IVAR_$_Person$_age, "_age", "q", 3, 8},
	 {(unsigned long int *)&OBJC_IVAR_$_Person$_height, "_height", "q", 3, 8}}
};

Category会生成一个_category_t结构体,这个结构体会在运行期间执行attachCategories操作将属性,协议,方法附加到class上。

static struct _category_t _OBJC_$_CATEGORY_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"Person",
	0, // &OBJC_CLASS_$_Person,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test1,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test1,
	0,
	(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Test1,
};

Category的作用

  • 覆写class内的方法,比如:我们要修改某个SDK的方法
  • 裁分class的业务逻辑,将业务分为多个category,已达到将业务下沉的目的。
  • 为class添加私有属性,隐藏具体的实现。

为什么要设计Load这个方法

  • load方法都会执行,不存在被覆盖的情况。
  • 其他我不知道的原因

Load方法耗时

待完善