review
We explored the _read_images function, looked at the class processing, looked at the actual readClass flow, and found that ro and RW were not loaded.
realizeClass
The introduction of
Because the main research is the class loading principle, so skip the intermediate process, directly look at the class loading
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if(! cls)continue;
const char *mangledName = cls->nonlazyMangledName();
const char *person = "LKTeacher";
if (strcmp(mangledName, person) == 0) {
printf("non-lazy classes ********%s**********",mangledName);
}
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage} realizeClassWithoutSwift(cls, nil); }}Copy the code
From apple’s comments, we can clearly know that only non-lazy loading will execute the following code, so we add +load methods to LKTeacher. In order to accurately locate LKTeacher class, we add the code used before
const char *mangledName = cls->nonlazyMangledName();
const char *person = "LKTeacher";
if (strcmp(mangledName, person) == 0) {
printf("non-lazy classes ********%s**********",mangledName);
}
Copy the code
Enter a breakpoint, enter debugging, and finally execute to the realizeClassWithoutSwift method
realizeClassWithoutSwift
To view
/*********************************************************************** * realizeClassWithoutSwift * Performs first-time initialization on class cls, * including allocating its read-write data. * Does not perform any Swift-side initialization. * Returns the real class structure for the class. * Locking: runtimeLock must be write-locked by the caller **********************************************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
// Omit the code
auto ro = (const class_ro_t *)cls->data(); // Get the data address in the 'machO file' and strong-shift it according to the 'class_ro_t' format.
auto isMeta = ro->flags & RO_META;// Check whether it is a metaclass
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.rw = cls->data(); ro = cls->data()->ro(); ASSERT(! isMeta); cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); }else {
// Normal class. Allocate writeable class data.
rw = objc::zalloc<class_rw_t>();// Initialize 'rw', copy 'ro' to 'rw', and set 'bits.data' of 'CLS' to 'rw'
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
// Omit the intermediate code
// Attach categories
methodizeClass(cls, previously);
return cls;
}
Copy the code
Add a breakpoint to the start of the method, printro
You can seebaseMethodList
There are no methods in it.realizeClassWithoutSwift
Will eventually be implementedmethodizeClass
Methods.
methodizeClass
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Omit the code
// Code that is written by itself, which is precisely targeted to its own class, and is not metaclass
const char *mangledName = cls->nonlazyMangledName();
const char *person = "LKTeacher";
if (strcmp(mangledName, person) == 0 && !isMeta) {
printf("non-lazy classes ********%s**********",mangledName);
}
/ / end
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
if (rwe) rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->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, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously, ATTACH_CLASS_AND_METACLASS); }}objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
}
Copy the code
Look at the whole code, debug the breakpoint, and find that the RWE does not have a value, so the corresponding criterion does not enter, so focus on the prepareMethodLists method.
prepareMethodLists
static void prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle, const char *why)
{
runtimeLock.assertLocked();
if (addedCount == 0) return;
// Omit the code
// Add method lists to array.
// Reallocate un-fixed method lists.
// The new methods are PREPENDED to the method list array.
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
ASSERT(mlist);
// Fixup selectors if necessary
if(! mlist->isFixedUp()) { fixupMethodList(mlist, methodsFromBundle,true/*sort*/); }}// If the class is initialized, then scan for method implementations
// tracked by the class's flags. If it's not initialized yet,
// then objc_class::setInitialized() will take care of it.
if (cls->isInitialized()) {
objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount); objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount); objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount); }}Copy the code
FixupMethodList (mlist, methodsFromBundle, true/*sort*/); Here we sort the methods, enter the methods
static void fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort){ runtimeLock.assertLocked(); ASSERT(! mlist->isFixedUp());// fixme lock less in attachMethodLists ?
// dyld3 may have already uniqued, but not sorted, the list
if(! mlist->isUniqued()) { mutex_locker_t lock(selLock);// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
printf("Unsorted ***%s-- %p***\n",name,meth.name()); meth.setName(sel_registerNameNoLock(name, bundleCopy)); }}// Sort by selector address.
// Don't try to sort small lists, as they're immutable.
// Don't try to sort big lists of nonstandard size, as stable_sort
// won't copy the entries properly.
if(sort && ! mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {method_t::SortBySELAddress sorter;
std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
}
printf("*** Dividing line ***\n");
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
printf("***%s-- %p****\n",name,meth.name());
}
// Mark method list as uniqued and sorted.
// Can't mark small lists, since they're immutable.
if (!mlist->isSmallList()) {
mlist->setFixedUp();
}
}
Copy the code
According to the Apple comment, you can see that this is sorting by selector address. Because the M1 device is small lists, it cannot be verified. If you need to verify, please debug it by yourself.
Load exploration of non-lazily loaded classes
We found the loading method for the classrealizeClassWithoutSwift
Add breakpoints here and remove themLKTeacher
The inside of theload
Method to view the stack informationYou can see that the whole process is as follows, that is, the class is loaded when it starts to be called
- lookUpImpOrForward
- realizeAndInitializeIfNeeded_locked
- initializeAndLeaveLocked
- initializeAndMaybeRelock
- realizeClassMaybeSwiftAndUnlock
- realizeClassMaybeSwiftMaybeRelock
- realizeClassWithoutSwift
Exploration of the nature of classification
Add LKPerson and the category LKPerson (LK) (written together for compilation purposes) to the main file, and add attributes and methods to the category
found_category_t
There is ainstance_methods
andclass_methods
This is because the classification has no metaclass, so both instance methods and class methods exist in the classification. The CPP file was not foundget
andset
Method, so you can see that the classification cannot add attributes.
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
WrappedPtr<method_list_t, PtrauthStrip> classMethods;
struct protocol_list_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);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else returnprotocols; }};Copy the code
rwe
When was the value assigned
Auto rwe = rw->ext(); , click on ext,
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
}
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
} else {
return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
}
}
class_rw_ext_t *deepCopy(const class_ro_t *ro) {
return extAlloc(ro, true);
}
Copy the code
Find the extAllocIfNeeded() method, search globally for extAllocIfNeeded, and find the following method calls
- attachCategories
- objc_class::demangledName
- class_setVersion
- addMethods_finish
- class_addProtocol
- _class_addProperty
- objc_duplicateClass