在文章OC底层探索(十二)_objc_init分析——类的加载(上)中,有介绍到_read_Image的readClass方法是没有对rw(read write)、ro(read only)、rwe(read write ext)进行操作,那么在哪对类进行操的呢?
类的加载
rw、ro和rwe
-
app在使用类时,是需要在磁盘中app的二进制文件中读取类的信息,二进制文件中的类存储了类的元类、父类、flags和方法缓存,如下图
-
那么类的额外信息(name、方法、协议和实例变量等)存储在class_ro_t中
-
class_ro_t简称ro:read only,将类从磁盘中读取到内存中就是对ro的赋值操作。由于ro是只读的,加载到内存后不会发生改变又称为clean memory(干净内存)。
-
class_rw_t简称rw:read write,用于读写编写程序。 drity memory 在进程运行时发生更改的内存。类一经使用运行时就会分配一个额外的内存,那么这个内存变成了drity memory。但是在实际应用中,类的使用量只是10%,这样就在rw中造成了内存浪费,所以苹果就把rw中方法、协议和实例变量等放到了class_rw_ext_t中。
👇
-
class_rw_ext_t简称rwe:read write ext,用于运行时存储类的方法、协议和实例变量等信息。
-
下图就是类在内存中存储的结构
分析如何、何时对ro、rw、rwe进行操作的
- 在_read_image源码中有一段代码是下面的内容,其注释是非懒加载类加载方法。
// +load handled by prepare_load_methods()
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
const char *mangledName = cls->mangledName();
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
auto kc_ro = (const class_ro_t *)cls->data();
printf("_getObjc2NonlazyClassList: 这个是我要研究的 %s \n",LGPersonName);
}
if (!cls) continue;
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);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
// Realize newly-resolved future classes, in case CF manipulates them
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
- 在iOS开发中,类都是懒加载的,只有用到类的时候才会将类加载到内存中,那么如何将类变成非懒加载类呢?在类中添加load方法
+ (void)load{
}
realizeClassWithoutSwift 方法:实现类
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
const char *mangledName = cls->mangledName();
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
auto kc_ro = (const class_ro_t *)cls->data();
auto kc_isMeta = kc_ro->flags & RO_META;
if (!kc_isMeta) {
printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName);
}
}
if (!cls) return nil;
if (cls->isRealized()) return cls;
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
//part1.在磁盘中将类的数据取出放入ro中
auto ro = (const class_ro_t *)cls->data();
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中
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
//part2:实现父类和元类的realizeClass 形成一个继承链
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
······
// Attach categories
//part3:实现类的方法
methodizeClass(cls, previously);
return cls;
}
- part1.在磁盘中将类的数据取出放入ro中,并且将数据也存储到rw中一份
//part1.在磁盘中将类的数据取出放入ro中
auto ro = (const class_ro_t *)cls->data();
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中
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
- part2:实现父类和元类的realizeClass 形成一个继承链
递归循环将类的父类和元类都实现realizeClassWithoutSwift方法,从而形成一个继承链。
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
- part3:实现类的方法
将类的方法等读取到rwe中去。
methodizeClass(cls, previously);
methodizeClass方法
- methodizeClass方法中对rwe进行了操作,但没有赋值,并且将方法进行了排序
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->mangledName();
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
bool kc_isMeta = cls->isMetaClass();
auto kc_rw = cls->data();
auto kc_ro = kc_rw->ro();
if (!kc_isMeta) {
printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName);
}
}
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
//排序、加载方法
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
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);
#if DEBUG
// Debug: sanity-check all SELs; log method list contents
for (const auto& meth : rw->methods()) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name));
}
ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name);
}
#endif
}
- prepareMethodLists:方法排序
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle)
{
runtimeLock.assertLocked();
const char *mangledName = cls->mangledName();
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
bool kc_isMeta = cls->isMetaClass();
auto kc_rw = cls->data();
auto kc_ro = kc_rw->ro();
if (!kc_isMeta) {
printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName);
}
}
if (addedCount == 0) return;
// There exist RR/AWZ/Core special cases for some class's base methods.
// But this code should never need to scan base methods for RR/AWZ/Core:
// default RR/AWZ/Core cannot be set before setInitialized().
// Therefore we need not handle any special cases here.
if (baseMethods) {
ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
}
// 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方法
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);
meth.name = sel_registerNameNoLock(name, bundleCopy);
}
}
// Sort by selector address.
if (sort) {
method_t::SortBySELAddress sorter;
std::stable_sort(mlist->begin(), mlist->end(), sorter);
}
// Mark method list as uniqued and sorted
mlist->setFixedUp();
}
- 在获取到方法、属性和协议后,并没有对rwe赋值
懒加载类与非懒加载类
在上面我们探索_read_image源码中,我们注意到了注释显示非懒加载类,执行ro、rw、rwe的赋值,那么懒加载在什么时候执行的呢?
-
1.删除load方法,将类变成懒加载类,
-
2.在methodizeClass方法打个断点
-
3.打印堆栈查看方法的调用,就会发现在执行loopUpImpForward时执行的methodizeClass方法。
-
验证
-
在
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
......
//当类没有实现时,需要实现类
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
......
return imp;
}
👇
- initializeAndLeaveLocked方法
// Locking: caller must hold runtimeLock; this may drop and re-acquire it
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
return initializeAndMaybeRelock(cls, obj, lock, true);
}
👇
- initializeAndMaybeRelock方法
static Class initializeAndMaybeRelock(Class cls, id inst,
mutex_t& lock, bool leaveLocked)
{
......
nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
// runtimeLock is now unlocked
// fixme Swift can't relocate the class today,
// but someday it will:
cls = object_getClass(nonmeta);
}
👇
- realizeClassMaybeSwiftAndUnlock方法
static Class
realizeClassMaybeSwiftAndUnlock(Class cls, mutex_t& lock)
{
return realizeClassMaybeSwiftMaybeRelock(cls, lock, false);
}
👇
- realizeClassMaybeSwiftMaybeRelock方法
static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
......
realizeClassWithoutSwift(cls, nil);
......
return cls;
}
- 最终执行到了 realizeClassWithoutSwift方法,所以懒加载类是在使用到类(初始化消息发送)的时候进行的加载
总结
懒加载类的加载流程:
_read_images -> lookUpImpOrForward -> realizeClassWithoutSwift -> methodizeClass
非懒加载类的加载流程:
_read_images -> realizeClassWithoutSwift -> methodizeClass
分类的加载
在realizeClassWithoutSwift方法中我们可以发现好多注释都是 Attach categories,那么分类是什么结构呢?
分类的本质
- 在程序中新建分类,并且clang,生成main.cpp文件,查看分类的结构:分类也是一个结构体。
- 在objc_4的源码中搜索category_t也可以看到category的源码
分类的加载流程
- attachToClass方法
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);
if (it != map.end()) {//主类没有实现 - 分类实现了load,就会执行到这
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);
}
}
👇
- attachCategories方法
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags)
{
if (slowpath(PrintReplacedMethods)) {
printReplacements(cls, cats_list, cats_count);
}
if (slowpath(PrintConnecting)) {
_objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
}
/*
* Only a few classes have more than 64 categories during launch.
* This uses a little stack, and avoids malloc.
*
* Categories must be added in the proper order, which is back
* to front. To do that with the chunking, we iterate cats_list
* from front to back, build up the local buffers backwards,
* and call attachLists on the chunks. attachLists prepends the
* lists, so the final result is in the expected order.
*/
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);
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);
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
- 其中我们发现rwe的赋值 rwe = cls->data()->extAllocIfNeeded();
在源码中我们发现extAllocIfNeeded -> extAlloc中对rwe进行了申请内存空间,并进行了赋值。
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 *>();
} else {
return extAlloc(v.get<const class_ro_t *>());
}
}
👇
class_rw_ext_t *
class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy)
{
runtimeLock.assertLocked();
auto rwe = objc::zalloc<class_rw_ext_t>();
rwe->version = (ro->flags & RO_META) ? 7 : 0;
method_list_t *list = ro->baseMethods();
if (list) {
if (deepCopy) list = list->duplicate();
rwe->methods.attachLists(&list, 1);
}
// See comments in objc_duplicateClass
// property lists and protocol lists historically
// have not been deep-copied
//
// This is probably wrong and ought to be fixed some day
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
set_ro_or_rwe(rwe, ro);
return rwe;
}
👇
在分析类的加载时,分析到了在methodizeClass方法中没有对rwe进行赋值,那么我们全局搜索一下extAllocIfNeeded方法的调用。
通过全局搜索能够看出rwe除了在添加分类以外,还在除了系统外的addMethod,addProtocol,addProperty这些地方用到过,正如WWDC里面所说的只有对原始的内存进行处理和修改的时候我们才会用到rwe。
- 其中我们只分析method_list_t ,(属性和协议都和方法类似)
👇
将分类的方法列表存储到mlists数组中。判断mcount是否等于ATTACH_BUFSIZ,表示最大修改次数为64次, 如果不等于则mlists[64 - 1] = mlist进行倒序插入
👇
然后执行attachLists方法
- attachLists方法:将分类加载到主类上
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) { //目前有多个list,又加入多个list
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
//将存在的多个list整段平移
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
//将新的lists存放到首位
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
//part2:向list数组中添加一个list,一维数组
list = addedLists[0];
}
else {//
// 1 list -> many lists
//part3:向list数组(目前只有一个元素)中添加多个list(list数组),二维数组
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;
//将oldlist放到最后面的位置
if (oldList) array()->lists[addedCount] = oldList;
//从0位置放addedLists,大小是addedCount * sizeof(array()->lists[0])
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
void tryFree() {
if (hasArray()) {
for (uint32_t i = 0; i < array()->count; i++) {
try_free(array()->lists[i]);
}
try_free(array());
}
else if (list) {
try_free(list);
}
}
template<typename Result>
Result duplicate() {
Result result;
if (hasArray()) {
array_t *a = array();
result.setArray((array_t *)memdup(a, a->byteSize()));
for (uint32_t i = 0; i < a->count; i++) {
result.array()->lists[i] = a->lists[i]->duplicate();
}
} else if (list) {
result.list = list->duplicate();
} else {
result.list = nil;
}
return result;
}
};
-
part1:目前有多个list,又加入多个list
lists中存在多个list,这时又要插入多个list,那么a. 生成newCount:总的list个数 b. 根据newCount创建一个新的Array c. memmove:将oldList整段平移 d.memcpy:将新的lists放到首位memcpy(参数1:从什么位置,参数2:插入什么,参数3:插入的长度)
-
part2:向list数组中添加一个list,一维数组
a. 想list数组中添加一个list
-
part3:向list数组(目前只有一个元素)中添加多个list(list数组),二维数组
a. 生成newCount:总的list个数 b. 根据newCount创建一个新的Array c. array()->lists[addedCount] = oldList;:将oldList放到末尾 d.memcpy:将新的lists放到首位memcpy(参数1:从什么位置,参数2:插入什么,参数3:插入的长度)
总结
分类的加载流程:
realizeClassWithoutSwift -> methodizeClass -> attachCategories -> attachLists
主类与分类加载数据的时机
分类attachCategories执行时机
分析:attachCategories方法在什么时候、什么地点调用的呢
-
全局搜索 attachCategories方法,发现只有在两个地方调用了
-
attachToClass方法中调用
- load_categories_nolock方法中调用
-
在这两个方法中打上断点就会发现是由load_categories_nolock -> 执行到的attachCategories方法中。
-
在attachCategories方法处打断点,并在控制台bt输出堆栈信息
-
那么在bt堆栈信息中我们看到 load_images -> loadAllCategories -> load_categories_nolock -> load_categories_nolock -> attachCategories
懒加载类/非懒加载类 和 懒加载分类/非懒加载分类
在上述分析中,主类和分类都实现了load方法,即都是非懒加载,那么如果有两个分类,其中主类是非懒加载类,分类有一个是懒加载类一个是非懒加载类呢?
或者主类是懒加载,分类是非懒加载类;又或者主类和分类都为懒加载类呢?
- 添加一个主类LGPerson,分类一:LGA,分类二:LGB
懒加载类与懒加载分类
- 在realizeClassWithoutSwift方法出打一个断点,并在控制台输入bt打印一下堆栈,就会发现在消息第一次调用时,进行加载
懒加载类与非懒加载分类
- 在attachToClass处打个断点,查看ro的内容,就会发现methodlist中的个数是8个,说明类加载了
- 继续向下执行就会发现执行attachCategories方法,开始加载分类
- 主类懒加载,分类为非懒加载,那么就会迫使主类提前加载 。
非懒加载类与懒加载分类
非懒加载类与两个懒加载分类
- LGPerson实现load方法,两个分类不实现load方法
- 在realizeClassWithoutSwift方法中打个断点,查看ro发现methodList的个数有16(主类有两个属性和四个方法,每个分类都有四个方法)个,只在data()中读取,并只处理一下相同方法名的函数。
非懒加载类与一个非懒加载分类和一个懒加载分类
-
LGA不实现Load方法,LGPerson和LGB添加load方法
-
在attachCategories方法的 method_list_t *mlist = entry.cat->methodsForMeta(isMeta);处打一个断点查看entry.cat的内容
-
第一次进入可以看到打印的name是LGA
- 此处还在验证