Class load (a) here is the main talk is the class load, this article discusses the next classification load

The problem first

1. Why can’t set and get methods be automatically generated for attributes added to a classification? 2. How many cases can classes and categories be loaded?

Resources to prepare

1, objC source code download openSource.apple.com/

classification

Nature of classification

By looking at the bottom line, we need to program the source code into C++ code for analysis

@interface ASSon (ext)

@property(nonatomic, copy)NSString *name;
@property(nonatomic, assign)NSInteger age;
- (void)instanceMethod1;
+ (void)instanceMethod2;

@end

@implementation ASSon (ext)

- (void)instanceMethod1 {}
+ (void)instanceMethod2 {}

@end
Copy the code

Clang-rewrite-objc-rewrite-objc main.m -o main-arm64. CPP complete compilation (no error) xcRun-sdk iphonesimulator clang -rewrite-objc -rewrite-objc main.m -o main-arm64.cpp

C + + analysis

Through clang compilation instruction, corresponding CPP files will be generated. We analyze app files from bottom to top (available information is mainly at the bottom).

static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
	&_OBJC_$_CATEGORY_ASSon_$_ext,
};
Copy the code

You can see that the classification is stored in the __objc_catlist of the __DATA section of the MachO file

static struct _category_t _OBJC_$_CATEGORY_ASSon_$_ext __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"ASSon",
	0, // &OBJC_CLASS_$_ASSon,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_ASSon_$_ext,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_ASSon_$_ext,
	0,
	(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_ASSon_$_ext,
};
static void OBJC_CATEGORY_SETUP_$_ASSon_$_ext(void ) {
	_OBJC_$_CATEGORY_ASSon_$_ext.cls = &OBJC_CLASS_$_ASSon;
}
Copy the code

You can see that the category_t structure type is _category_t, and then search _category_t to see that it is a structure as follows

struct _category_t {
	const char *name;
	struct _class_t *cls;
	const struct _method_list_t *instance_methods;
	const struct _method_list_t *class_methods;
	const struct _protocol_list_t *protocols;
	const struct _prop_list_t *properties;
};
Copy the code
  • This structure has someThe class name.cls.List of object methods.List of class methods.Agreement list.Property listBut noList of variablesThis is also the category attribute withoutset.getThe reason for the method.
  • Classification of instance methods and class methods is separated in two_method_list_tIn, because there is no meta-classification of classification, classification method is inThe runtimethroughattachToClassInserted into theclass.
static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_ASSon_$_ext __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	1,
	{{(struct objc_selector *)"instanceMethod1", "v16@0:8", (void *)_I_ASSon_ext_instanceMethod1}}
};

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_ASSon_$_ext __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	1,
	{{(struct objc_selector *)"instanceMethod2", "v16@0:8", (void *)_C_ASSon_ext_instanceMethod2}}
};

static struct /*_prop_list_t*/ {
	unsigned int entsize;  // sizeof(struct _prop_t)
	unsigned int count_of_properties;
	struct _prop_t prop_list[2];
} _OBJC_$_PROP_LIST_ASSon_$_ext __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_prop_t),
	2,
	{{"name","T@\"NSString\",C,N"},
	{"age","Tq,N"}}
};
Copy the code

There is an object method and a class method of the form sel+ signature + address, just like the method_t structure. We find variable names for attributes, but no corresponding set and get methods.

conclusion

  • The structure type at the bottom of the classification is_category_t
  • Classes can add methods and attributes, not member variables
  • The attribute added by category does not existsetandgetMethod implementation
  • The classification has two method lists, indicating that the classification has no meta-classification to store methods

Classification loading

Loading time analysis

MethodizeClass ->attachToClass->attachCategories; methodizeClass->attachToClass->attachCategories It is discovered that attachCategories are the core method for category loading.

The attachToClass method mainly adds the classification to the main class

void attachToClass(Class cls, Class previously, int flags) { runtimeLock.assertLocked(); ASSERT((flags & ATTACH_CLASS) || (flags & ATTACH_METACLASS) || (flags & ATTACH_CLASS_AND_METACLASS)); auto &map = get(); Auto it = map.find(previously); When the main class does not implement the load method, the class starts loading, forcing the main class to start loading if (it! = map.end()) { category_list &list = it->second; if (flags & ATTACH_CLASS_AND_METACLASS) { int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS; attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS); attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS); } else { attachCategories(cls, list.array(), list.count(), flags); } map.erase(it); }}Copy the code

The attachCategories method initializes THE RWE, which mainly involves addMethod, addProperty, and AddProtocol. That is, the RWE is initialized only when the original class is modified or processed

static void attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count, Int flags) {/// code omitted...... /// The maximum number of categories is 64 constexpr uint32_t ATTACH_BUFSIZ = 64; method_list_t *mlists[ATTACH_BUFSIZ]; property_list_t *proplists[ATTACH_BUFSIZ]; protocol_list_t *protolists[ATTACH_BUFSIZ]; uint32_t mcount = 0; uint32_t propcount = 0; uint32_t protocount = 0; bool fromBundle = NO; bool isMeta = (flags & ATTACH_METACLASS); Auto rwe = CLS ->data()->extAllocIfNeeded(); for (uint32_t i = 0; i < cats_count; i++) { auto& entry = cats_list[i]; Method_list_t *mlist = entry.cat->methodsForMeta(isMeta); if (mlist) { if (mcount == ATTACH_BUFSIZ) { prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__); rwe->methods.attachLists(mlists, mcount); mcount = 0; } mlists[ATTACH_BUFSIZ - ++mcount] = mlist; fromBundle |= entry.hi->isBundle(); } /// property_list_t *proplist = entry.cat->propertiesForMeta(isMeta, entry.hi); if (proplist) { if (propcount == ATTACH_BUFSIZ) { rwe->properties.attachLists(proplists, propcount); propcount = 0; } proplists[ATTACH_BUFSIZ - ++propcount] = proplist; } /// protocol_list_t *protolist = entry. Cat ->protocolsForMeta(isMeta); if (protolist) { if (protocount == ATTACH_BUFSIZ) { rwe->protocols.attachLists(protolists, protocount); protocount = 0; } protolists[ATTACH_BUFSIZ - ++protocount] = protolist; } } if (mcount > 0) { prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle, __func__); rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount); if (flags & ATTACH_EXISTING) { flushCaches(cls, __func__, [](Class c){ // constant caches have been dealt with in prepareMethodLists // if the class still is constant here, it's fine to keep return ! c->cache.isConstantOptimizedCache(); }); }}Copy the code

AttachLists adds classification data to the main class

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; array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount)); newArray->count = newCount; array()->count = newCount; for (int i = oldCount - 1; i >= 0; i--) newArray->lists[i + addedCount] = array()->lists[i]; for (unsigned i = 0; i < addedCount; i++) newArray->lists[i] = addedLists[i]; free(array()); setArray(newArray); validate(); } else if (! list && addedCount == 1) { // 0 lists -> 1 list list = addedLists[0]; validate(); } else { // 1 list -> many lists Ptr<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; for (unsigned i = 0; i < addedCount; i++) array()->lists[i] = addedLists[i]; validate(); }}Copy the code

Conclusion: Classification loading is divided into three steps

  • Classification data loading timing: according toWhether classes and classifications are implementedloadmethodsTo distinguish between different times
  • attachCategoriesPreparing classification Data
  • attachListswillClassification data is added to the main classIn the

Loading Process Analysis

By global searchattachCategoriesThere are only two places where it’s called,attachToClassandload_categories_nolock

Global searchload_categories_nolockThere are only two places to call itloadAllCategoriesand_read_images.loadAllCategoriesThere’s only one place to call itload_images.

Global searchattachToClassThere’s only one place to call itmethodizeClass.

Through the above analysis, it is also known that the loading times of classes and categories are equalloadMethod implementation. Therefore, four case collocation analysis of class and classification can be carried out. Non-lazy loading classes and non-lazy loading classification

Calling method path

  • Class loadingmap_images->map_images_nolock->_read_images->readClass-> _getObjc2NonlazyClassList->realizeClassWithoutSwift->methodizeClass
  • Classification loadingload_images->loadAllCategories->load_categories_nolock->attachCategories->attachLists

Non-lazy-loaded classes and lazy-loaded classes call the method path

  • Class loadingmap_images->map_images_nolock->_read_images->readClass-> _getObjc2NonlazyClassList->realizeClassWithoutSwift->methodizeClass
  • Classification loadingmap_images->map_images_nolock->_read_images->readClass-> _getObjc2NonlazyClassList->realizeClassWithoutSwift->methodizeClass->attachToClass->attachCategories->attachLists

Lazy-loaded classes and lazy-loaded classes call method paths

  • Class loadinglookUpImpOrForward->realizeAndInitializeIfNeeded_locked->realizeClassMaybeSwiftAndLeaveLocked->realizeClassMaybeSwiftMaybeRelock->realizeClassWithoutSwift->methodizeClass
  • Classification loadinglookUpImpOrForward->realizeAndInitializeIfNeeded_locked->realizeClassMaybeSwiftAndLeaveLocked->realizeClassMaybeSwiftMaybeRelock->realizeClassWithoutSwift->methodizeClass->attachToClass->attachCategories->attachLists

Lazy-loaded and non-lazy-loaded classes call the method path

  • Class loadingmap_images->map_images_nolock->_read_images->readClass-> _getObjc2NonlazyClassList->realizeClassWithoutSwift->methodizeClass
  • Classification loadingload_images->loadAllCategories->load_categories_nolock->attachCategories->attachLists

The following conclusions can be drawn from the path analysis above:

  • Non-lazy-loaded classes+Non-lazy load classification: class loading in_read_images, the classification of the loading inload_imagesMethod, the class is loaded first, and classified information is added to the class
  • Non-lazy-loaded classes+Lazy load classification: class loading in_read_images, the loading of classification is inCompile time
  • Lazy loading class+Lazy load classification: class loading inThe first message is sentWhen, the classification load is inCompile time
  • Lazy loading class+Non-lazy load classification: As long as the classification is implementedload, will force the main classLoading in advanceWhich is consistent with the first case

Answer questions first

1. Why can’t set and get methods be automatically generated for attributes added to a classification? As you can see from the _category_t structure, there is no list of member variables in the structure, which is why added attributes do not automatically generate set, get methods. 2. How many cases can classes and categories be loaded? See loading -> Loading process analysis for the above categories