As mentioned in the previous article, classes that implement the + load method are not lazy-loaded classes, or otherwise they are.
- Non-lazy loading classes:
+ load method
Is called before main. This time to be able to guarantee+ load method
To be called, the class must be loaded in advance.
- Non-lazy class loading process:
_dyld_objc_notify_register()
->read_image
->RealizeClassWithoutSwift)
->methodizeClass
->attachLists
Assign to RW.
- Lazy loading: As the name implies, is not normally loaded, only loaded when needed.
So how do lazy log classes load?
Lazy loading of classes
The lazy-loaded class is actually loaded the first time we use it, when the first message is sent to the class.
We also talked about message sending in previous chapters, and one of the most important methods in message sending is lookUpImpOrForward. We mentioned that! CLS ->isRealized() used to initialize lazily loaded classes. In the object-c environment, after a series of function calls, it will magically come to the realizeClassWithoutSwift that we learned in the previous article.
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
...
if(! cls->isRealized()) { cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock); // runtimeLock may have been dropped but is now locked again } ... } static Class realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) {return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}
static Class
realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
{
return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}
static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
lock.assertLocked();
if(! cls->isSwiftStable_ButAllowLegacyForNow()) { // Non-Swift class. Realize it now with the lock still held. // fixme wrongin the future for objc subclasses of swift classes
realizeClassWithoutSwift(cls);
if(! leaveLocked) lock.unlock(); }else {
// Swift class. We need to drop locks and call the Swift
// runtime to initialize it.
lock.unlock();
cls = realizeSwiftClass(cls);
assert(cls->isRealized()); // callback must have provoked realization
if (leaveLocked) lock.lock();
}
return cls;
}
Copy the code
Let’s test if lazy loading classes can be satisfied! CLS – > isRealized () conditions
1.1 test a
AKPerson does not implement the + load method. It is a lazy load class. The main program calls the initialization method of [AKPerson alloc].
Conclusion 1: The lazy-loaded class is loaded when it is first called, in the lookUpImpOrForward method in the message lookup process.
1.2 test two
The parent class AKPerson implements the + load method, while the subclass AKStudnet does not. The cache is cleared, and the main program subclass calls the initialization method.
Conclusion 2: The parent class implements + load, and the child class does not implement + load. The parent class is a non-lazy-loaded class and the subclass is a lazy-loaded class.
1.3 test three
The parent class AKPerson does not implement the + load method. The subclass AKStudnet implements the + load method. Cleanup cache, initialization method called by the main program subclass first, and initialization method called by the parent class.
Found that the parent class did not enter! CLS ->isRealized(), whose parent is a lazy loaded class. Because recursive calls to realizeClassWithoutSwift complete the inheritance chain and deal with the parent, metaclass of the current class; If there is a parent class, add the current class to the subclass list
if(! cls)returnnil; . supercls = realizeClassWithoutSwift(remapClass(cls->superclass)); metacls = realizeClassWithoutSwift(remapClass(cls->ISA())); . // Update superclass and metaclassin caseof remapping cls->superclass = supercls; cls->initClassIsa(metacls); . // Connect this class to its superclass`s subclass listsif (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
Copy the code
Conclusion 3: If the subclass implements + load, the parent class will also be loaded when the subclass is loaded. The reason for this is that subclasses are processed by their parents and metaclasses when they are loaded.
Ii. Classification structure
2.1 clang
Create a new AKPerson + Test category
clang -rewrite-objc AKPerson+Test.m -o category.cpp
- Category is stored in the MachO file
The __ __DATA
theobjc_catlist
In the
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_AKPerson_$_Test};Copy the code
- The structure of the AKPerson category is as follows
static struct _category_t _OBJC_$_CATEGORY_AKPerson_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"AKPerson",
0, // &OBJC_CLASS_$_AKPerson,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_AKPerson_$_Test,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_AKPerson_$_Test, 0, 0};Copy the code
2.2 Structure of classification
Search for category_t in objc source
struct category_t { const char *name; // The name of the class, not the name of the class classref_t CLS; Struct method_list_t *instanceMethods; Struct method_list_t *classMethods; Struct protocol_t *protocols; Struct property_list_t *instanceProperties; // Fields below this point are not always present on disk. struct property_list_t *_classProperties; Method_list_t *methodsForMeta(bool isMeta) {if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
Copy the code
Why should classification keep instance methods and class methods separate?
In the process of class and metaclass loading, the instance method exists in the class, and the class method exists in the metaclass, and the place where the method belongs has been determined. Classification is later than the loading of classes and metaclasses.
Three. Classification loading
Now that we know that classes are lazy-loaded and non-lazy-loaded, their loading timing is different, so what about the loading of classes?
Before we analyze this, we need to make it clear that a class must be attached to a class. If there are only classes and no classes, then it does not logically make sense and even if it is implemented, the compiler will ignore it.
Classification loading occurs in two places:
_read_images
theDiscover categories.
methodizeClass
.
// Discover categories. // Discover all categoriesforCategory_t ** CATList = _getObjc2CategoryList(hi, &count); bool hasClassProperties = hi->info()->hasCategoryClassProperties();for(i = 0; i < count; I ++) {// Inner loop over all Category category_t *cat = catList [I]; Class cls = remapClass(cat->cls); // First, register a Category with the class to which it belongs. If the class is already implemented, the list of methods for the class is reconstructed. bool classExists = NO;if(the cat - > instanceMethods | | cat - > separate protocols | | cat - > instanceProperties) {/ / add a Category to the corresponding value of the Class, Value is the Class all the corresponding category array addUnattachedCategoryForClass (cat, CLS, hi); // Add method, protocol, and property for the Category to the Classif (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : ""); } // Add a Category to the Meta Class. // Add a Category to the Classif (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); }}}}Copy the code
static void methodizeClass(Class cls)
{
...
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/); . }Copy the code
We added some debugging code to facilitate positioning
3.1 Classification of lazy loading
Lazy loading class & lazy loading classification
- Send a message to lazily loaded classes,
lookupOrForward
->realizeClassWithoutSwift
Start loading memory methodizeClass
Handle parent and metaclass relationshipsunattachedCategoriesForClass
Returns NULL- Another place to load the classification is not called
Non-lazy loading & lazy loading classification
After two breakpoint debugging, we found that the lazy classification was not added at runtime. Let’s see if the methods in the classification were added.
- Program started
dyld
->_objc_init
->map_images
->_read_images
->realizeClassWithoutSwift
->methodizeClass
Load the class into memory methodizeClass
Handle parent and metaclass relationshipsunattachedCategoriesForClas
Returns NULL- Another place to load the classification is not called
Lazy-loaded categories are not added at runtime, so let’s see if methods in the categories are added.
- Take a look at class_rw_t
(lldb) p *$3
(class_rw_t) $4 = {
flags = 2148139008
version = 7
ro = 0x00000001000011f0
methods = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x0000000100001168
arrayAndFlag = 4294971752
}
}
}
properties = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x0000000000000000
arrayAndFlag = 0
}
}
}
protocols = {
list_array_tt<unsigned long, protocol_list_t> = {
= {
list = 0x0000000000000000
arrayAndFlag = 0
}
}
}
firstSubclass = nil
nextSiblingClass = 0x00007fff92d22080
demangledName = 0x0000000000000000
}
Copy the code
- Continue looking at baseMethodList in RO
(lldb) p $8.get(1)
(method_t) $15 = {
name = "load"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000c10 (objc-debug`+[AKPerson load] at AKPerson.m:12)
}
(lldb) p $8.get(2)
(method_t) $16 = {
name = "cate_instanceMethod"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000da0 (objc-debug`+[AKPerson(test) cate_instanceMethod] at AKPerson+test.m:34)
}
Copy the code
Through the LLDB debugging described above, we found that the methods in our classification have been added to ro.
Conclusion: The lazy-loaded classification is determined at compile time for both lazy-loaded and non-lazy-loaded classes.
3.2 Classification of non-lazy loading
Lazy loading classes & non-lazy loading classes
According to the previous theory, lazily loaded classes were loaded the first time a message was sent, The function call stack should be lookupImpOrForward – > realizeClassMaybeSwiftAndLeaveLocked – > realizeClassMaybeSwiftMaybeRelock – > RealizeClassWithoutSwift – > methodizeClass. Let’s test that out.
This time by unattachedCategoriesForClass take to value, and before that CLS ro no classification in the initialize method:
But what about our function call stack, which instead of sending messages goes through the prepare_load_methods method of load_images?
- Lazy loading classes are not loaded until the message is sent.
- But the classification is not lazy load class, the classification will go ahead of time
read_images
->addUnattachedCategoryForClass
- There is no implementation class at this point, it will be below
prepare_load_methods
->realizeClassWithoutSwift
->unattachedCategoriesForClass
The implementation class information is advanced
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
extern bool hasLoadMethods(const headerType *mhdr);
extern void prepare_load_methods(const headerType *mhdr);
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if(! hasLoadMethods((const headerType *)mh))return; recursive_mutex_locker_t lock(loadMethodLock); // Discover load methods { mutex_locker_t lock2(runtimeLock); prepare_load_methods((const headerType *)mh); } // Call +load methods (without runtimeLock - re-entrant) call_load_methods(); } void prepare_load_methods(const headerType *mhdr) { size_t count, i; runtimeLock.assertLocked(); Classref_t * classList = _getObjc2NonlazyClassList(MHDR, &count); classref_t * classList = _getObjc2NonlazyClassList(MHDR, &count);for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
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
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods"); } realizeClassWithoutSwift(cls); assert(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); }}Copy the code
_getObjc2NonlazyCategoryList
Get all of the non-lazy classes, and then iterate over those non-lazy classes, loading the classes that those classes depend on.realizeClassWithoutSwift
Method to load a class
Conclusion: The non-lazily loaded class implementation is ahead of schedule, so lazily loaded classes are not necessarily loaded only on the first message sent, but also depending on whether there is a non-lazily loaded class. If there is a non-lazily loaded class, then go
load_images
The inside of theprepare_load_methods
的realizeClassWithoutSwift
。
Non-lazy loading classes & non-lazy loading classes
The non-lazy loading process we are familiar with, loading in _read_images, is also non-lazy loading.
methodizeClass
Place a breakpoint:
unattachedCategoriesForClass
_read_images
的Discover categories
Place a breakpoint
remethodizeClass
/***********************************************************************
* remethodizeClass
* Attach outstanding categories to an existing class.
* Fixes up cls`s method list, protocol list, and property list.
* Updates method caches for cls and its subclasses.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
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); }}Copy the code
remethodizeClass
There is aattachCategories
methods
// 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);
}
Copy the code
Note the English notes:
- Attach method lists and properties and protocols from categories to a class. Attach method lists and properties and protocols from categories to a class
- Assuming the categories in cats are all loaded and sorted by load order, — the world Assumes that the categories are loaded in the load order
- Oldest categories first
The principle of attachCategories is basically the same as that of attachLists (refer to the loading of classes) :
- call
attachLists
Add classification methods, attributes, and protocols memmove
Move the original data to the endmemcpy
Copy the new data to the starting location
Note:
- Actually,
attachCategories
This method will only be implemented againNon-lazy load classification
Next will be called, and comeattachCategories
Before, depending on whether the class is lazily loaded, - If it’s lazy loading, then load_images is handled,
- If the load is not lazy, it is handled in read_images.
4. Classification summary
- Class loading
- Non-lazy loading classes:
+ load method
Is called before main. This time to be able to guarantee+ load method
To be called, the class must be loaded in advance. - Lazy loading: As the name implies, is not normally loaded, only loaded when needed
- Classification loading:
- Non-lazy load classification: not implemented
load
Method, determined at compile time, directly handles data() -ro. - Lazy load separation: achieved
load
Method, determined at runtime.
- This also shows that the loading of categories is different from the loading of classes. Combining the two, we have the following conclusions:
scenario | Class loading | Classification loading |
---|---|---|
Lazy load classification + lazy load class | First send | Compile time |
Lazy loading class + non-lazy loading class | _read_images | Compile time |
Non-lazy loading class + lazy loading class | Load_images (non-lazy-loaded classification brings forward our lazy-loaded class implementation) | MethodizeClass after class loading |
Non-lazy loading class + non-lazy loading class | _read_images | ReMethodizeClass after class loading |
5. Contention on the namesake methods of classes and classifications
If a class has multiple categories, what is the order of method calls?
[AKPerson alloc] sayHi]; [AKPerson alloc] sayHi]; [AKPerson alloc] sayHi]; .
- Classification is not implemented
+ load
methods
Response Compile Sources for the last classification
- Classification is implemented
+ load
methods
Compile Sources
AKPerson+Test1
implementation+ load
Method,AKPerson+Test2
Don’t realize+ load
methods
AKPerson+Test2
implementation+ load
Method,AKPerson+Test1
Don’t realize+ load
methods
+ load
Conclusion 1:
The general method calls the class first and then the main class.
- Instead of replacing the existing methods of the class, the classified methods are placed in front of the new list of methods, and the methods of the class are placed behind the new list of methods. This is what we normally call “overwriting” the methods of the class with the same name
- Because the runtime looks up methods in the order of the list of methods, it returns the IMP as soon as it finds a method with the corresponding name.
Conclusion 2:
- If the classification is not implemented
+load
Method, then the responseCompile Sources
The last category - If both implement +load, respond ·Compile Sources· the last classification
- If one of them is implemented
+load
Method, the response is not lazy load classification. Lazy classes are loaded into memory at compile time, not at run time
Six load_images.
In the case of lazy loading class + non-lazy loading class, load_image is called when the class is loaded into memory, so we explore this case.
When we press a breakpoint on the load_image implementation, we find that neither the class nor the class prints the +load method: load_image precedes the +load method
Note the English notes:
- Discover load methods —
prepare_load_methods
- Call +load methods (without runtimeLock – re-entrant) — call_load_methods
6.1 prepare_load_methods Discover and prepare the +load method
void prepare_load_methods(const headerType *mhdr) { size_t count, i; runtimeLock.assertLocked(); Classref_t * classList = _getObjc2NonlazyClassList(MHDR, &count);for(i = 0; i < count; i++) { schedule_class_load(remapClass(classlist[i])); } / / 2. Get the lazy loading classification list category_t * * categorylist = _getObjc2NonlazyCategoryList (MHDR, & count);for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
const class_ro_t *ro = (const class_ro_t *)cls->data();
const char *cname = ro->name;
const char *oname = "AKPerson";
if (cname && (strcmp(cname, oname) == 0)) {
printf("_getObjc2NonlazyClassList Class name :%s - %p Class name :%s \n",cname,cls,cat->name);
}
if(! cls)continue; // category for ignored weak-linked class
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods"); } realizeClassWithoutSwift(cls); assert(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); }}Copy the code
/***********************************************************************
* prepare_load_methods
* Schedule +load for classes in this image, any un-+load-ed
* superclasses in other images, and any categories in this image.
**********************************************************************/
// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
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
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
Copy the code
/***********************************************************************
* add_category_to_loadable_list
* Category cat`s parent class exists and the category has been attached
* to its class. Schedule this category for+load after its parent class * becomes connected and has its own +load method called. **********************************************************************/ void add_category_to_loadable_list(Category cat) { IMP method; loadMethodLock.assertLocked(); method = _category_getLoadMethod(cat); // Don`t botherif cat has no +load method
if(! method)return;
if (PrintLoading) {
_objc_inform("LOAD: category '%s(%s)' scheduled for +load",
_category_getClassName(cat), _category_getName(cat));
}
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *)
realloc(loadable_categories,
loadable_categories_allocated *
sizeof(struct loadable_category));
}
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
Copy the code
Prepare_load_methods analysis:
_getObjc2NonlazyClassList
To obtainNon-lazy-loaded classes
The list ofschedule_class_load
Walk through the list of classes
- To recursively call the parent class
+ load
Method to ensure that the parent class+ load
Methods are ordered before subclasses add_class_to_loadable_list
把Class + load
methodsloadable_classes
inside
_getObjc2NonlazyCategoryList
To obtainNon-lazy load classification
The list of- Iterate over the category list
realizeClassWithoutSwift
To prevent the class from not being initialized (if it is already initialized)add_category_to_loadable_list
把Classification of + load
Methods toloadable_categories
6.2 call_load_methods
Now we know that + load is called in load_images, so how does that happen?
/***********************************************************************
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first.
* Category +load methods are not called until after the parent class`s +load.
*
* This method must be RE-ENTRANT, because a +load could trigger
* more image mapping. In addition, the superclass-first ordering
* must be preserved in the face of re-entrant calls. Therefore,
* only the OUTERMOST call of this function will do anything, and
* that call will handle all loadable classes, even those generated
* while it was running.
*
* The sequence below preserves +load ordering in the face of
* image loading during a +load, and make sure that no
* +load method is forgotten because it was added during
* a +load call.
* Sequence:
* 1. Repeatedly call class +loads until there aren`t any more
* 2. Call category +loads ONCE.
* 3. Run more +loads if: * (a) there are more classes to load, OR * (b) there are some potential category +loads that have * still never been attempted. * Category +loads are only run once to ensure"parent class first"
* ordering, even if a category +load triggers a new loadable class
* and a new loadable category attached to that class.
*
* Locking: loadMethodLock must be held by the caller
* All other locks must not be held.
**********************************************************************/
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren`t any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
Copy the code
static void call_class_loads(void)
{
...
// Call all +loads for the detached list.
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;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging()); } (*load_method)(cls, SEL_load); }... }Copy the code
static bool call_category_loads(void)
{
...
// Call all +loads for the detached list.
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()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n", cls->nameForLogging(), _category_getName(cat)); } (*load_method)(cls, SEL_load); cats[i].cat = nil; }}...return new_categories_added;
}
Copy the code
- through
objc_autoreleasePoolPush
Push an automatic release pool - The do while loop
- Cycle call
call_load_methods
, send a message to invoke the class+load
Methods. - call
call_category_loads
, looped to send messages calling classified+load
Methods. - (*load_method)(cls, SEL_load); The procedure for calling + load is the procedure for objc_msgSend(CLS, SEL_load).
- through
objc_autoreleasePoolPop
Push out an automatic release pool+load
methods
7. The initialize
7.1 the initialize principle
Initializes the class before it receives its first message.
Called before the class receives the first message. When the class is not in use, the method may never be called.
It was found in lookUpImpOrForward -> initializeAndLeaveLocked -> initializeAndMaybeRelock -> initializeNonMetaClass.
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
void initializeNonMetaClass(Class cls)
{
...
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if(supercls && ! supercls->isInitialized()) { initializeNonMetaClass(supercls); }... callInitialize(cls); . }Copy the code
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
Copy the code
- If there is a parent class and there is no parent class
isInitialized
, recursiveinitializeNonMetaClass
Parent class (conjecture: call order first parent class after child class) callInitialize
Is a normal message sending (presumably: call order classification overrides the main class)
7.2 Initialize Call Sequence Test
- Is it superparent or subclass
Both the AKPerson parent and AKTeacher subclasses implement the Initialize method
- The main program calls first
The parent class
, after the callA subclass
- The main program calls first
A subclass
, after the callThe parent class
- Is the classification overwriting the main class
Both AKPerson + Test and AKTeacher + Test classes implement the initialize method. The main program calls the subclass and the parent class respectively to initialize the method.
- Is it only called once
The AKPerson parent class implements the initialize method. The AKTeacher subclass does not implement the initialize method. The main program calls the subclass initialization method.
7.3 the initialize summary
initialize
Use the normal message sending mechanism. So the classification overrides the main class when more than one classification is implementedinitialize
Method that performs the classification method that is last loaded into memory.initialize
Called before the first method of the class or its subclass is called (before the message is sent)- If both parent and child classes are implemented
initialize
Method, when calling a subclass,
- if
Initialize the parent class
Method is called, onlyA subclass of the initialize
Methods; - if
Initialize the parent class
If no, call firstInitialize the parent class
Method in the callA subclass of the initialize
Methods. (The initialize method is no longer called when the parent class is reinitialized.)
- Parent class implementation, child class not implementation, when calling a child class, will be called twice
Initialize the parent class
methods
Eight. Summary
This article mainly studied the lazy loading class non-lazy loading class lazy loading classification non-lazy loading classification loading; + load and + initialize calls. And that’s where the percentage is asked in the interview, hopefully that helps.