+load +initialize
+load方法
在app启动的时候各个类的+load方法都会被调用,+load方法不是通过消息机制调用的,它是直接调用的,因此无论是在子类或者category中复写此方法,复写的+load方法都会被调用,查看runtime的代码可知调用的顺序为
父类->子类->分类。(原因是runtime会把所有类的+load方法放到一个列表中,然后按顺序调用,所有category的+load方法放到一个列表中,然后也按顺序调用,所以当所有类的+load方法都执行完成后才开始去执行分类的+load方法)。所有类的+load方法在调用列表中的顺序由两个因素决定,一个是此类文件的编译顺序,一个是递归调用的顺序,即从父类到子类。所有分类的+load方法在调用列表中的顺序只由编译顺序决定。
schedule_class_load
call_class_load
runtime代码如下:
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
prepare_load_methods
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) {
call_class_loads();
}
// 2. Call category +loads ONCE
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;
}
+initialize
与+load方法不同,+initialize方法是通过消息的方式发送的(msgSend),所以+initialize方法可以被子类或者分类覆盖,每个类第一次被用到的时候,runtime都会调用 _class_initialize(Class cls)方法,_class_initialize(Class cls)也是一个递归调用的方法,当发现cls的父类没有调用此方法时,会递归调用_class_initialize(cls->supercls),调用时会向该类发送initialize消息,此方法被调用完成后cls->isInitialized()这个属性会变成true。
注意这里存在一个类的initialize方法被调用多次的可能。当子类没有实现initialize方法时,父类的initialize方法可能被调用多次。
举个例子:
有两个类:Person,Girl,Girl继承Person
如果只有Person实现了initialize方法,那么当这Girl类被用到时,Person的这个initialize方法会被调用两次,一次是父类调用_class_initialize(Person)时发送的,另一次是调用_class_initialize(Girl)时发送的,因为给Girl发送initialize的时候发现Girl并没有实现此方法,所以此方法被转发到了Girl的父类,即Person的类对象。
可以这么理解,每个类对应的_class_initialize(Class cls)这个方法只会被调用一次,调用的顺序为从父类到子类,在_class_initialize(Class cls)这个方法中会向当前的类对象发送initialize方法。如果当前类没有实现initialize方法则依次去父类找。
_class_initialize(Class cls)
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
Class supercls;
BOOL reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: calling +[%s initialize]",
cls->nameForLogging());
}
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
if (PrintInitializing) {
_objc_inform("INITIALIZE: finished +[%s initialize]",
cls->nameForLogging());
}
// Done initializing.
// If the superclass is also done initializing, then update
// the info bits and notify waiting threads.
// If not, update them later. (This can happen if this +initialize
// was itself triggered from inside a superclass +initialize.)
monitor_locker_t lock(classInitLock);
if (!supercls || supercls->isInitialized()) {
_finishInitializing(cls, supercls);
} else {
_finishInitializingAfter(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
// We couldn't set INITIALIZING because INITIALIZING was already set.
// If this thread set it earlier, continue normally.
// If some other thread set it, block until initialize is done.
// It's ok if INITIALIZING changes to INITIALIZED while we're here,
// because we safely check for INITIALIZED inside the lock
// before blocking.
if (_thisThreadIsInitializingClass(cls)) {
return;
} else {
monitor_locker_t lock(classInitLock);
while (!cls->isInitialized()) {
classInitLock.wait();
}
return;
}
}
else if (cls->isInitialized()) {
// Set CLS_INITIALIZING failed because someone else already
// initialized the class. Continue normally.
// NOTE this check must come AFTER the ISINITIALIZING case.
// Otherwise: Another thread is initializing this class. ISINITIALIZED
// is false. Skip this clause. Then the other thread finishes
// initialization and sets INITIALIZING=no and INITIALIZED=yes.
// Skip the ISINITIALIZING clause. Die horribly.
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
+load +initialize的更多相关文章
- +load,+initialize原理
+load,+initialize原理 1.load 父类的load方法在子类load方法之前调用,分类的load方法在原来类load方法之后调用,依赖类的load方法会在自己之前调用,总之所有的类的 ...
- WARN bzip2.Bzip2Factory: Failed to load/initialize native-bzip2 library system-native, will use pure-Java version
[root@hdp2 /root]#hadoop checknative -a 18/12/09 00:31:19 WARN bzip2.Bzip2Factory: Failed to load/in ...
- Objective c, +load, +initialize 方法
+load() 当类被加载入程序的时候会执行+load方法 +initialize() 当类第一次被使用的时候会执行+initialize方法 这两个方法都只会被执行一次.
- 细说OC中的load和initialize方法
OC中有两个特殊的类方法,分别是load和initialize.本文总结一下这两个方法的区别于联系.使用场景和注意事项.Demo可以在我的Github上找到--load和initialize,如果觉得 ...
- iOS---Objective-C: +load vs +initialize
在 NSObject 类中有两个非常特殊的类方法 +load 和 +initialize ,用于类的初始化.这两个看似非常简单的类方法在许多方面会让人感到困惑,比如: 子类.父类.分类中的相应方法什么 ...
- +load 和 +initialize
APP 启动到执行 main 函数之前,程序就执行了很多代码. 执行顺序: 将程序依赖的动态链接库加载到内存 加载可执行文件中的所有符号,代码 runtime 解析被编译的符号代码 遍历所有的 cla ...
- Google Map API V3开发(3)
Google Map API V3开发(1) Google Map API V3开发(2) Google Map API V3开发(3) Google Map API V3开发(4) Google M ...
- Google 地图 API V3 之事件
Google官方教程: Google 地图 API V3 使用入门 Google 地图 API V3 针对移动设备进行开发 Google 地图 API V3 之事件 Google 地图 API V3 ...
- Google 地图 API V3 之控件
Google官方教程: Google 地图 API V3 使用入门 Google 地图 API V3 针对移动设备进行开发 Google 地图 API V3 之事件 Google 地图 API V3 ...
随机推荐
- vultr上 windows使用pptp拨号来实现冗余双网关的解决方案
rasdial是拨号程序,pptpvpn是网卡拨号名称,后面跟的是帐号和密码.pptpvpn见下图:就是提前创建好一个PPTP的拨号连接 上面是启动时候的计划任务,那么万一拨号中断,要继续重拨还需要做 ...
- SqlServer查询中使用事务
--获取一个新的ID declare @newId bigint select @newId=MAX(ID) from BdRdRecord01 begin tran or @newId is nul ...
- 删除win7任务栏通知区域图标的方法
大家都知道程序运行后会在任务栏的通知区域显示表明正在运行,但是有很多失效的图标也会在此显示,那么怎么样删除那些没用的图标呢? 1.在开始运行里输入:regedit进入注册表编辑器 2.进入注册表编辑器 ...
- Jensen不等式
- CRM 2016 执行IFrame 子页面中函数
CRM代码: var iframe = Xrm.Page.getControl("IFRAME_xxx").getObject(); iframe.contentWindow.Re ...
- 第32课 Linux内核链表剖析
1. Linux内核链表的位置及依赖 (1)位置:{linux-2.6.39}\\include\linux\list.h (2)依赖 ①#include<linux\types.h> ② ...
- C# 线程 在 sleep,suspend 之后 Abort 的方法
1) 线程在sleep时的Abort 方法:对线程函数用 catch ThreadAbortException ,并return. 示例: [csharp] view plaincopy ...
- SQL注入之代码层防御
[目录] 0x0 前言 0x1 领域驱动的安全 1.1 领域驱动的设计 1.2 领域驱动的安全示例 0x2 使用参数化查询 2.1 参数化查询 2.2 Java中的参数化语句 2.3 .NET(C#) ...
- .NET MVC 保存Session值,6位数验证码
//6位数验证码: Random rm = new Random(); , ).ToString(); //MVC控制器Action中 保存session值 System.Web.HttpContex ...
- centos磁盘挂载|centos虚拟机硬盘不够怎么办?|centos虚拟机硬盘的扩展
Centos6磁盘挂载 添加一块磁盘 分区,格式化,挂载新磁盘 磁盘挂载 df -lh fdisk -l fdisk /dev/sdb 这个命令执行后依次输 n p 回车 回车 w fdisk -l ...