load方法:
當一個類或者該類的分類被加入Objective-C
運行時的時候被調(diào)用牵署。load method
調(diào)用的時機是非常早的喧半,所以你不應該在該方法中去引用其他你自定義的對象挺据,因為你沒辦法去判斷該對象是否已經(jīng)進行 load method
。當然暇检,你可以使用該類所依賴的frameworks
婉称,比如Foundation
,在你調(diào)用load method
方法時這些框架已經(jīng)確保被完全加載成功悔据。當然你的父類也會在這前加載成功瘫筐。
load調(diào)用時機:
1.類本身的load method
在所有父類的load method
調(diào)用后調(diào)用。
2.分類的load method
在父類的load method
調(diào)用前調(diào)用肛捍。
3.類本身load method
在分類的load method
前調(diào)用拙毫。
我們現(xiàn)在建立4個文件,在每個文件里面添加load method
,并在load method
里面打印一句話:
不做任何操作直接
bulid
峭跳,我們來看打印臺的log
:這樣的結果就證明了我們前面的結論衅码。
源碼證明調(diào)用時機:
你可以點擊
這里下載runtime源碼
逝段。這里下載的是objc4-706.tar.gz
版本。
dyld 是the dynamic link editor
的縮寫帚桩,動態(tài)鏈接器.主要的任務是為了生成可執(zhí)行文件嘹黔。
更多了解可以點擊這里
1.引導程序初始化:
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_2_images, load_images, unmap_image);
}
2.獲取在類的列表和分類的列表参淹,如果沒有則return;如果獲取到則上鎖調(diào)用prepare_load_methods
void load_images(const char *path __unused, const struct mach_header *mh)
{
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
rwlock_writer_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
3.prepare_load_methods
方法中你可以看到先遍歷了類的列表而不是分類的乏悄,所以類的load method
會先調(diào)用檩小。schedule_class_load
方法中你可以看到如果父類有load method
,會遞歸的去遍歷筐付,add_class_to_loadable_list
然后加入到待加載列表阻肿。所以父類的load method
方法會比子類先調(diào)用。而_getObjc2NonlazyCategoryList
直接遍歷不做任何操作所以子類的在父類的前面较解。
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++) {
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);
}
}
//遞歸加入到到待加載列表
static void schedule_class_load(Class cls)
{
if (cls->info & CLS_LOADED) return;
if (cls->superclass) schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->info |= CLS_LOADED;
}
Initialize方法
runtime
會發(fā)送initialize message
當初始化一個類印衔。父類會比子類先接受到這個消息。這個操作是線程安全的瞎暑,這就是說与帆,當你在某一個線程A里面初始化這個類玄糟,這個線程A會去發(fā)送Initialize message
給這個類對象。如果其他線程B這個時候要發(fā)送其他的消息給這個類嫂拴,線程B會阻塞直到線程A處理Initialize消息完成贮喧。
Initialize調(diào)用時機:
1.父類會比子類先接受到這個消息。
2.如果子類沒有實現(xiàn)initialize mothod
,而父類實現(xiàn)了辩恼。那么父類的Initialize mothod
將會調(diào)用多次谓形。
我們現(xiàn)在在'Person'和'Student'文件里面重寫initialize method
,讓后在'viewDidLoad method'添加下面的代碼:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@ %s 當前的線程:%@", [self class], __FUNCTION__,[NSThread currentThread]);
Student *Student1 = [[Student alloc] init];
Student *Student2 = [[Student alloc] init];
});
}
bulid
項目寒跳,我們來看打印臺的log
:
你會發(fā)現(xiàn):
1.我們做了兩次初始化但第二次初始化
Student
并沒有觸動initialize method
童太。2.父類的
initialize method
方法比子類的先調(diào)用。3.
Student
初始化的線程和initialize method
線程是相同的翘贮。
現(xiàn)在我們注釋掉子類的initialize method
爆惧,再次bulid:
這個時候
Person
父類調(diào)用了兩次扯再。
源碼證明調(diào)用時機:
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());
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
@try {
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: finished +[%s initialize]",
cls->nameForLogging());
}
}
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: +[%s initialize] threw an exception",
cls->nameForLogging());
}
@throw;
}
@finally {
// 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 {
waitForInitializeToComplete(cls);
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!");
}
}
void _class_initialize(Class cls) 方法里面下面這段代碼就是為了遞歸保證先執(zhí)行父類的class_initialize method傲隶。
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
下面的代碼就是為了確認當本類沒有實現(xiàn)這個class_initialize method窃页,會再次調(diào)用父類的
if (PrintInitializing) {
_objc_inform("INITIALIZE: calling +[%s initialize]",
cls->nameForLogging());
}