load相关问题
Category中有load方法吗?
有load方法。
调用时机
load方法在runtime加载类、分类的时候调用,调用方式是直接调用方法,而不是通过消息机制触发调用。
load 方法能继承吗?
load
方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用。
调用顺序
-
先调用类的
load
方法。按照编译先后顺序调用,先编译的先调用。
-
调用子类的
load
方法之前会先调用父类的load
方法。 -
再调用分类的
load
方法。按照编译先后顺序调用,先编译的先调用。
源码
// objc-os.mm/_objc_init(void)
void _objc_init(void)
{
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
// objc-runtime-new.mm/load_images
void load_images(const char *path __unused, const struct mach_header *mh)
{
prepare_load_methods((const headerType *)mh);
call_load_methods();
}
// objc-runtime-new.mm / prepare_load_methods
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertWriting();
//获取所有的类
classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// 整理要调用load方法的类
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
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
// objc-runtime-new.mm / schedule_class_load
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
//类是否已经被加载到loadable_classes
if (cls->data()->flags & RW_LOADED) return;
// 确保父类排在前面
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
// 标志该类已经添加到了loadable_classes
cls->setInfo(RW_LOADED);
}
// objc-loadmethod.mm / add_class_to_loadable_list
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
//objc-loadmethod.mm / call_load_methods
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) {
//调用类的load方法
call_class_loads();
}
// 2. Call category +loads ONCE
// 调用Category的load方法
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;
}
//objc-loadmethod.mm / call_class_loads
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
// 取出load方法
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方法。
(*load_method)(cls, SEL_load);
}
// Destroy the detached list.
if (classes) free(classes);
}
initialize 相关问题
调用时机
initialize
会在类第一次接受到消息的时候调用。与load
方法不同的是initialize
是通过消息机制调用的即通过objc_msgsend()
调用。
调用顺序
根据调用顺序触发。
调用子类的initialize
会先触发父类的initialize
。
// objc-runtime-new.mm / class_getInstanceMethod
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
lookUpImpOrNil(cls, sel, nil,
NO/*initialize*/, NO/*cache*/, YES/*resolver*/);
return _class_getMethod(cls, sel);
}
// objc-runtime-new.mm / lookUpImpOrNil
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
if (imp == _objc_msgForward_impcache) return nil;
else return imp;
}
// objc-runtime-new.mm / lookUpImpOrForward
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
runtimeLock.read();
if (initialize && !cls->isInitialized()) {
runtimeLock.unlockRead();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.read();
}
return imp;
}
// objc-initialize.mm / _class_initialize
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
supercls = cls->superclass;
// 递归调用,先调用父类的 initialize
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
callInitialize(cls);
}
// objc-initialize.mm / callInitialize
void callInitialize(Class cls)
{
// 通过 objc_msgSend 进行调用
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
总结
load、initialize方法的区别什么?
调用方式
- load是根据函数地址直接调用
- initialize是通过objc_msgSend调用
调用时刻
- load是runtime加载类、分类的时候调用(只会调用1次)
- initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次)
load、initialize的调用顺序?
-
load
- 先调用类的load
- 先编译的类,优先调用load
- 调用子类的load之前,会先调用父类的load
- 先调用类的load
-
再调用分类的load
- 先编译的分类,优先调用load
-
initialize
- 先初始化父类
- 再初始化子类(可能最终调用的是父类的initialize方法)
今天的文章load VS initialize分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/19242.html