標籤:
寫在前面
近幾天花了一些時間瞭解了一下Objective-C runtime相關的東西,其中涉及到了+load方法,譬如method swizzling通常在category的+load方法中完成。之前對initializer和load的使用就比較疑惑,但一直沒有詳細去對比瞭解,以此為契機,集各方資源,分析一下吧!
關於瞭解+initialize
和+load
,個人感覺參考官方文檔《NSObject Class Reference》就夠了。
+initialize
關於+initialize
方法,《NSObject Class Reference》的介紹如下:
Initializes the class before it receives its first message.
可以理解+initialize的
作用是為了該Class在使用前建立合適的環境;
關於其使用,《NSObject Class Reference》的說明如下:
The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses. The superclass implementation may be called multiple times if subclasses do not implement initialize—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize].
這上面這段話,可以得出如下這麼一些意思:
+initialize
方法是在runtime被調用的;
- 對於某個類,其類
+initialize
方法都會在該對象接受任何訊息之前被調用;
- 如果父類和子類的+initialize方法都被調用,父類的調用一定在子類之前,這是系統自動完成的,子類+initialize中沒必要顯式調用
[super initialize];
;
- runtime系統處理+initialize訊息的方式是安全執行緒的,所以沒必要在+initialize中為了保證安全執行緒而使用lock、mutex之類的安全執行緒工具;
- 某個類的+initialize的方法不一定只被調用一次,至少有兩種情況會被調用多次:
- 子類顯式調用
[super initialize];
;
- 子類沒有實現+initialize方法;
下面以樣本示範某個類的+initialize
被多次執行的現象。
定義三個類:Person、Student、Teacher,Student和Teacher繼承自Person,Person繼承自NSObject。Person和Student都實現了+initialize
方法,Teacher沒有實現該方法,如下:
// Person的+initialize方法的實現+ (void)initialize { NSLog(@"Person initialize");} // Student的+initialize方法的實現+ (void)initialize { NSLog(@"Student initialize");}
執行效果如下:
- (void)viewDidLoad { Student *aStudent = [[Student alloc] init]; Teacher *aTeacher = [[Teacher alloc] init]; [super viewDidLoad];} /* 輸出:Person initializeStudent initializePerson initialize*/
可以看到,對於Student,在其+initialize
方法被調用之前,其super class(Person)的+initialize
方法被率先調用;對於Teacher,沒有定義+initialize
方法,所以它會直接調用super class(Person)的+initialize
方法,這就導致了Person的+initialize
方法被執行兩次。
有沒有辦法避免Person的+initialize
方法被多次調用?當然可以:
// Person的+initialize方法的實現+ (void)initialize { static BOOL b = false; if (!b) { NSLog(@"Person initialize"); b = true; }}
也可以這樣:
// Person的+initialize方法的實現+ (void)initialize { if (self == [Person class]) { NSLog(@"Person initialize"); }}
《NSObject Class Reference》中還對+initialize
方法的使用做了一些警告:
Because initialize is called in a thread-safe manner and the order of initialize being called on different classes is not guaranteed, it’s important to do the minimum amount of work necessary in initialize methods. Specifically, any code that takes locks that might be required by other classes in their initialize methods is liable to lead to deadlocks. Therefore you should not rely on initialize for complex initialization, and should instead limit it to straightforward, class local initialization.
總結一下,就是這樣:
那麼+initialize可以做些什麼事情呢?可以做一些簡單的初始化工作,譬如對於某個繼承自UICollectionViewCell的自訂類PhotoViewCell,PhotoViewCell的對象可能會有一些公用資源,譬如label color,label font等等,沒必要在-initXXOO方法中建立這些完全一樣的資源,此時就可以放在PhotoViewCell中的+initialize中完成,如下:
+ (void)initialize { titleFont = [UIFont systemFontOfSize:12]; titleHeight = 20.0f; videoIcon = [UIImage imageNamed:@"CTAssetsPickerVideo"]; titleColor = [UIColor whiteColor]; checkedIcon = [UIImage imageNamed:@"CTAssetsPickerChecked"]; selectedColor = [UIColor colorWithWhite:1 alpha:0.3];}
+initialize
終究還是帶來驚人的資訊量,頗為失望。
+load
二者都戶只執行一次(在不考慮開發人員主動使用的情況下,系統最多會調用一次);
load在類被載入到系統時執行;
如果父類和子類都被調用,父類的調用一定在子類之前
都是為了應用運行提前建立合適的運行環境
在使用時都不要過重地依賴於這兩個方法,除非真正必要
關於+load
方法,《NSObject Class Reference》的介紹如下:
Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.
關於其使用,《NSObject Class Reference》的說明如下:
The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.
The order of initialization is as follows:
- All initializers in any framework you link to.
- All +load methods in your image.
- All C++ static initializers and C/C++ attribute(constructor) functions in your image.
- All initializers in frameworks that link to you.
In addition:
- A class’s +load method is called after all of its superclasses’ +load methods.
- A category +load method is called after the class’s own +load method.
In a custom implementation of load you can therefore safely message other unrelated classes from the same image, but any load methods implemented by those classes may not have run yet.
從這段文字可以讀出如下資訊:
- 在一個程式(main函數)運行之前,所用到的庫被載入到runtime之後,被添加到的runtime系統的各種類和category的
+load
方法就被調用;(關於這點很容易通過列印語句來驗證);
- 如果父類和子類的+load方法都被調用,父類的調用一定在子類之前,這是系統自動完成的,子類+load中沒必要顯式調用
[super load];
;
- 文檔沒有講明+load的執行是否是安全執行緒的,但考慮到它是在runtime之前就調用,所以談論它是否是安全執行緒沒啥必要,根據我的理解,多線程在runtime才有談論意義;
- 若某個類由一個主類和多個category組成,則允許主類和category中各自有自己的+load方法,只是category中的+load的執行在主類的+load之後;
關於+load
的使用情境,筆者知道的至少有一個,method swizzling的處理一般都在category的+load
中完成的,參考這裡。
參考
- 《NSObject Class Reference》;
Objective-C中的+initialize和+load