回顾
前面探索到_read_images
函数,并查看了类的处理,查看了readClass
实际流程,发现并没有加载到ro
和rw
。
realizeClass
的引入
因为主要研究的是类的加载原理,所以跳过中间流程,直接查看类的加载
// 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);
}
}
通过苹果注释,我们可以明确知道,只有非懒加载才会执行下面这段代码,所以我们给LKTeacher
添加+load methods
,为了精准定位到LKTeacher
类,我们添加前面使用过的代码
const char *mangledName = cls->nonlazyMangledName();
const char *person = "LKTeacher";
if (strcmp(mangledName, person) == 0) {
printf("non-lazy classes ********%s**********",mangledName);
}
打上断点,进入调试,最终执行到realizeClassWithoutSwift
方法
realizeClassWithoutSwift
查看
/***********************************************************************
* 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)
{
//省略代码
auto ro = (const class_ro_t *)cls->data(); //获取`machO文件`中的数据地址,按照`class_ro_t`格式进行强转。
auto isMeta = ro->flags & RO_META;//判断是否为元类
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>();//初始化`rw`,将`ro`复制给`rw`,并将`cls`的`bits.data`设置为`rw`
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
//省略中间代码
// Attach categories
methodizeClass(cls, previously);
return cls;
}
添加断点到方法开始处,打印ro
可以看到baseMethodList
里面并没有任何方法。realizeClassWithoutSwift
最终会执行methodizeClass
方法。
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();
//代码省略
// 自己写的代码,精准定位到自己的类,并且不是元类
const char *mangledName = cls->nonlazyMangledName();
const char *person = "LKTeacher";
if (strcmp(mangledName, person) == 0 && !isMeta) {
printf("non-lazy classes ********%s**********",mangledName);
}
//结束
// 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);
}
查看整段代码,断点调试,发现rwe
并没有值,所以对应的判断条件也不会进入,所以重点查看prepareMethodLists
方法。
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;
//代码省略
// 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);
}
}
查看代码,fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
,这里对方法进行了排序,进入方法
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("未排序***%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("***分界线***\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();
}
}
根据苹果注释,可以看到这里是根据selector address
进行排序。因为M1
设备是small lists
,无法验证,如需验证,请自行调试。
非懒加载类的加载探索
类的加载方法我们找到了realizeClassWithoutSwift
,在此处添加断点,并去掉LKTeacher
里面的load
方法,查看堆栈信息
可以发现整个流程如下,也就是类开始调用时才会加载
- lookUpImpOrForward
- realizeAndInitializeIfNeeded_locked
- initializeAndLeaveLocked
- initializeAndMaybeRelock
- realizeClassMaybeSwiftAndUnlock
- realizeClassMaybeSwiftMaybeRelock
- realizeClassWithoutSwift
分类的本质探索
在main
文件里面添加LKPerson
和分类LKPerson (LK)
(为了方便编译写在一起),给分类添加属性和方法
发现_category_t
里面有instance_methods
和class_methods
,这是因为分类没有元类,所以实例方法和类方法,都存在分类里。查看cpp文件,并没有找到get
和set
方法,所以可以得知,分类不能添加属性。
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 return protocols;
}
};
rwe
什么时候赋值的
在methodizeClass
方法里面,可以看到auto rwe = rw->ext();
,点进去查看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);
}
找到extAllocIfNeeded()
方法,全局搜索extAllocIfNeeded
,发现以下方法调用
- attachCategories
- objc_class::demangledName
- class_setVersion
- addMethods_finish
- class_addProtocol
- _class_addProperty
- objc_duplicateClass