Summary of ios low-level articles
1. Image loading: map_images –> map_images_NOLock –> _read_images
The _objc_init injection callback _dyLD_OBJc_notify_register (&map_images, load_images, unmap_image) shows that the image is loaded in map_images
The call stack:map_images --> map_images_nolock --> _read_images
Read_images main function
-
- Condition control for a load
-
- Fixed precompile phase
@selector
The chaotic problem of
- Fixed precompile phase
-
- Wrong messy class handling
-
- Fixed remapping some classes that were not loaded by the image file
-
- Fix some messages
-
- When we have a protocol in our class: readProtocol
-
- Fix protocols that were not loaded
-
- Classification process
-
- Class loading processing
-
- Classes that are not processed optimize those that are violated
Class 2 loading
Print CLS before readClass. CLS does not have a name. Print CLS after readclassreadclass
That’s the key to class loading
Note: how to find the focus —- breakpoint debugging, judging and setting breakpoints in the loop, see if the program will enter the breakpoint, if not, you can directly over, only focus on the running process of the program
2.1 readClass analysis
Breakpoint debugging
- Define LGPerson
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface LGPerson : NSObject
@property (nonatomic.copy) NSString *kc_name;
@property (nonatomic.assign) int kc_age;
- (void)kc_instanceMethod1;
- (void)kc_instanceMethod2;
- (void)kc_instanceMethod3;
+ (void)kc_sayClassMethod;
@end
NS_ASSUME_NONNULL_END
#import "LGPerson.h"
@implementation LGPerson
//+ (void)load{
//
/ /}
- (void)kc_instanceMethod2{
NSLog(@"%s",__func__);
}
- (void)kc_instanceMethod1{
NSLog(@"%s",__func__);
}
- (void)kc_instanceMethod3{
NSLog(@"%s",__func__);
}
+ (void)kc_sayClassMethod{
NSLog(@"%s",__func__);
}
@end
Copy the code
Called in main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
[person kc_instanceMethod1];
NSLog(@"%p",person);
}
return 0;
}
Copy the code
Set breakpoints in the if and for functions of readClass, focusing on the loading part of the class
The readClass function does not go into the class’s ro, rW assignment if, but just addNamedClass and addClassTableEntry, which adds the class to the table
When does the class load process (assignment of ro, Rw,rwe) take place?
RealizeClassWithoutSwift exists in the macho structure data before the class-loading implementation
To use a class, app needs to read the class information in the binary file of app on disk. The binary file stores the metaclass, parent class, flags, and method cache of the class
- Class_ro_t ro: read only. Reading a class from disk into memory is an assignment to ro. Since RO is read-only, it does not change after loading into memory, also known as clean memory.
- Class_rw_t rW for short: read write, used for reading and writing programs. Drity Memory Memory that changes while the process is running. When a class is used, the runtime allocates an extra memory, which becomes drity memory.
- Class_rw_ext_t rWE for short: Read write ext, used at runtime to store information about class methods, protocols, and instance variables. In practice, only 10% of the classes are used, which is a waste of memory in the RW, so Apple puts the methods, protocols, and instance variables in the RW into class_rw_ext_t
- Get ro
const class_ro_t *ro(a) const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t* > ())) {return v.get<class_rw_ext_t *>()->ro;// If there is an rwe, take ro from rwe
}
return v.get<const class_ro_t* > ();// If there is no rwe, take ro from rw
}
Copy the code
- When do YOU need RWE
When analyzing the source code of attachCategroriesrwe = cls->data()->extAllocIfNeeded()
ExtAllocIfNeeded (We can search all of extAllocIfNeeded directly to see which areas can be controlled by developers
- attachCategories Adds categories - addMethod Adds methods - class_addProtocol Adds protocols - _class_addProperty Adds attributesCopy the code
2.2 realizeClassWithoutSwift(class load) call timing
-
Going back to the _read_images function, looking down at the function below, I see a familiar function, realizeClassWithoutSwift, which is the implementation class from its name, Breakpoint validation — but realizeClassWithoutSwift is not called in _read_images
-
Enter realizeClassWithoutSwift source code to view, is indeed the implementation of the class operation
Data () is read from the MachO structure of the class and assigned to ro and Rw
- Can be found in
ReaizeClassWithoutSwift Sets a breakpoint
And,Locate the classes that need attention
, explore when to call reaizeClassWithoutSwift
- The breakpoint is called in the main function
LGPerson *person = [LGPerson alloc]
Before callingreaizeClassWithoutSwift
.
The call stack is
- ReaizeClassWithoutSwift is called when a message is sent at a slower speed
lookUpImpOrForward
,
CLS = initializeAndLeaveLocked(CLS, INst, runtimeLock)—- This is a well-known lazy loading mechanism in OC that delays the loading of a class until the first method call
Question: When do you load a class from the realizeClassWithoutSwift function in _read_images?
We can implement the +load function in LGPerson, break the realizeClassWithoutSwift function in _read_images, and you can see that the breakpoint can block the main, and load the LGPerson class before we go into main
2.3 Lazy-loaded and Non-lazy-loaded Classes – Whether the current class implements load
- Lazy class case Data loading is delayed until the first message (slow lookup of a method to determine if the class is implemented)
Call stack: [LGPerson alloc] –> objc_alloc –>callAlloc –> _objc_msgSend_uncached –>lookUpImpOrForward –>initializeAndLeaveLocked–>initializeAndMaybeRelock–>realizeClassMaybeSwiftAndUnlock–>realizeClassMaybeSwiftMaybeRe lock –> realizeClassWithoutSwift
- Load all class data when map_images is not lazily loaded
The call stack:_dyld_start --> _objc_init --> _dyld_objc_notify_register --> dyld::registerObjcNotifiers --> dyld::notityBatchPartial --> map_images -->map_images_nolock --> _read_images --> realizeClassWithoutSwift
3 methodizeClass analysis (methodizeClass)
RealizeClassWithoutSwift function class after the data load is complete, will enterMethodizeClass function
.Processing and sorting of methods, properties, protocols, etc
Key function call stack:methodizeClass --> prepareMethodLists --> fixupMethodList
4 load_images parsing
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if(! didInitialAttachCategories && didCallDyldNotifyRegister) { didInitialAttachCategories =true;
loadAllCategories(a); }// 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(a); }void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked(a);classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
category_t * const *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, nil);
ASSERT(cls->ISA() - >isRealized());
add_category_to_loadable_list(cat); }}void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked(a);// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush(a);do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads(a); }// 2. Call category +loads ONCE
more_categories = call_category_loads(a);// 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