initialize被呼叫次數探究
概況
我們都知道+initialize
方法會在此類第一次被使用的時候會被呼叫,那麼呼叫的次數是靠什麼來決定的了?
結論一:類第一次被使用的時候,會先呼叫+initialize
方法
NSObject關於initialize原始碼
原始碼地址: https://github.com/RetVal/objc-runtime.git
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
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);
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
// 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: thread %p: calling +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
//
// Only __OBJC2__ adds these handlers. !__OBJC2__ has a
// bootstrapping problem of this versus CF's call to
// objc_exception_set_functions().
#if __OBJC2__
@try
#endif
{
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}
#if __OBJC2__
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
pthread_self(), cls->nameForLogging());
}
@throw;
}
@finally
#endif
{
// Done initializing.
lockAndFinishInitializing(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 if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
return;
} else {
// We're on the child side of fork(), facing a class that
// was initializing by some other thread when fork() was called.
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
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!");
}
}
先不考慮加鎖和多執行緒問題,_class_initialize
主要的就是如下兩個步驟:
- 如果父類沒有初始化,那麼遞迴初始化父類
- 初始化自己
第二步會呼叫:
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
看到objc_msgSend
表明:
結論二:會走OC的訊息傳送流程
根據以上的推論我們可以得知:+initialize
會被呼叫0次,一次,多次
- 呼叫0次:表明這個類沒有被使用到
- 呼叫1次:表明只有這個類被使用到,它的子類要不然是沒有被使用到,要不然就是有這個子類自己的
+initialize
(子類中+initialize
中沒有[super initialize]
程式碼)方法 - 呼叫多次:表明這個類或者它的多個子類被使用了,並且這多個子類沒有自己的
+initialize
方法,子類會根據OC訊息傳送流程而呼叫了它父類的+initialize
方法。
或者可以這麼理解,任何類在使用之前都會呼叫它的+initialize
,如果這個類沒有+initialize
,那麼就找它的父類的+initialize
一直到NSObject
類。並且不會自動呼叫 [super initialize]
。
Demo
為了驗證上述的結果,做了如下的測試
呼叫0次
類:RXInitializeParentObject
所有DemoObject的父類
@implementation RXInitializeParentObject
+ (void)initialize {
NSLog(@"Parent initialize");
}
- (void)print {
}
@end
測試類:RXInitializeTestObject
- (void)test_doNoting {
}
當然是什麼也沒有輸出
呼叫多次
RXInitializeEmptyObject
,子類沒有自己的initialize
@implementation RXInitializeEmptyObject
@end
測試類:RXInitializeTestObject
- (void)test_empty {
self.rxInitializeEmptyObject = [RXInitializeEmptyObject new];
[self.rxInitializeEmptyObject print];
}
輸出的結果:
Parent initialize
Parent initialize
第一行輸出:是因為初始化RXInitializeEmptyObject
會先初始化其父類RXInitializeParentObject
第二行輸出:對於RXInitializeEmptyObject
來說,根據OC的訊息傳送規則,它的initialize
方法定位到了父類的initialize
方法了
子類自定義的+initialize
方法
類RXInitializeCustomObject
@implementation RXInitializeCustomObject
+ (void)initialize {
NSLog(@"Custom initialize");
}
@end
測試類:RXInitializeTestObject
- (void)test_custom {
self.rxInitializeCustomObject = [RXInitializeCustomObject new];
[self.rxInitializeCustomObject print];
}
輸出結果
Parent initialize
Custom initialize
第一行輸出:是因為初始化RXInitializeCustomObject
會先初始化其父類RXInitializeParentObject
第二行輸出:對於RXInitializeCustomObject
來說,它有自己的initialize
方法。
先使用Empty再使用Custom
測試類:RXInitializeTestObject
- (void)test_empty_custom {
self.rxInitializeEmptyObject = [RXInitializeEmptyObject new];
[self.rxInitializeEmptyObject print];
self.rxInitializeCustomObject = [RXInitializeCustomObject new];
[self.rxInitializeCustomObject print];
}
輸出結果
Parent initialize
Parent initialize
Custom initialize
第一行輸出,是因為使用RXInitializeEmptyObject
初始化其父類
第二行輸出,是因為使用RXInitializeEmptyObject
初始化自己
第三行輸出,是因為使用RXInitializeCustomObject
先使用Custom再使用Empty
測試類:RXInitializeTestObject
- (void)test_custom_empty {
self.rxInitializeCustomObject = [RXInitializeCustomObject new];
[self.rxInitializeCustomObject print];
self.rxInitializeEmptyObject = [RXInitializeEmptyObject new];
[self.rxInitializeEmptyObject print];
}
輸出結果
Parent initialize
Custom initialize
Parent initialize
大家可以自己嘗試分析一下~~~
Custom & Custom2
類RXInitializeCustom2Object
,內容跟RXInitializeCustomObject
幾乎一樣
@implementation RXInitializeCustom2Object
+ (void)initialize {
NSLog(@"Custom 2 initialize");
}
@end
測試類:RXInitializeTestObject
- (void)test_custom_custom2
{
self.rxInitializeCustomObject = [RXInitializeCustomObject new];
[self.rxInitializeCustomObject print];
self.rxInitializeCustom2Object = [RXInitializeCustom2Object new];
[self.rxInitializeCustom2Object print];
}
輸出結果:
Parent initialize
Custom initialize
Custom2 initialize
結果也是很容易分析的。
super Custom
類RXInitializeSuperCustomObject
@implementation RXInitializeSuperCustomObject
+ (void)initialize
{
[super initialize];
NSLog(@"Super Custom initialize");
}
@end
測試類:RXInitializeTestObject
- (void)test_superCustom {
self.rxInitializeSuperCustomObject = [RXInitializeSuperCustomObject new];
[self.rxInitializeSuperCustomObject print];
}
輸出結果
Parent initialize
Parent initialize
Super Custom initialize
其中第二行是因為[super initialize]
導致的。
以上就是關於+initialize
方法被呼叫次數探究,包括理論部分和Demo部分。
相關文章
- +load和+initialize方法呼叫時機
- redis實現閘道器限流(限制API呼叫次數1000次/分)RedisAPI
- load與initialize
- String的又一次探究
- WinMain是如何被呼叫的AI
- 防止API被惡意呼叫API
- 帝國CMS網站登入次數超過5次賬號被鎖定網站
- 記一次mpvue-loader原始碼探究Vue原始碼
- 深入探究JVM之方法呼叫及Lambda表示式實現原理JVM
- Remote_login_passwordfile引數探究REM
- 呼叫MapReduce對檔案中單詞出現次數進行統計
- Runtime原始碼 +load 和 +initialize原始碼
- NSObject 的 initialize 和 load 方法Object
- ObjC load與initialize 簡析OBJ
- Vuex原始碼學習(七)action和mutation如何被呼叫的(呼叫篇)Vue原始碼
- [20200620]IMPDP TRANSFORM引數再探究.txtORM
- Failed to initialize NVML: Driver/library version mismatchAI
- initialize方法與load方法比較
- 教你在Nodejs中如何獲取當前函式被呼叫的行數及檔名NodeJS函式
- 探究kubernetes 探針引數periodSeconds和timeoutSeconds
- Pickswise:2020年6月至今運動員被罵次數排名 詹姆斯第一
- Category的本質<二>load,initialize方法Go
- **呼叫MapReduce對檔案中各個單詞出現的次數進行統計**
- Oracle 12.2中的索引統計被呼叫情況Oracle索引
- 物件呼叫動態變數物件變數
- NHK:71個領域中國科學論文被引用次數世界第一
- iOS倒數計時的探究與選擇iOS
- 記一次對webpack打包後程式碼的失敗探究Web
- Golang原始碼學習:使用gdb除錯探究Golang函式呼叫棧結構Golang原始碼除錯函式
- 一次被割韭菜的覆盤
- NSObject +(void)load 和+(void)initialize 方法的理解Object
- 解決直播商城原始碼中,getView被重複呼叫原始碼View
- 如何找到ABAP裡被動態呼叫的update function moduleFunction
- 最少操作次數
- 【轉】vue mounted 呼叫兩次的解決辦法Vue
- 在Linux中呼叫MapReduce對檔案中各個單詞出現次數進行統計Linux
- 輸出字串中出現次數最多的字元和次數字串字元
- Android 應用防止被二次打包指南Android