面試題引發(fā)的思考:
Q: Category中load
方法是什么時候調(diào)用的胀屿?load
方法能繼承嗎帕胆?
-
load
方法在Runtime加載類轴捎、分類的時候調(diào)用;
即程序啟動的時候就調(diào)用各個類的load方法百炬; load
方法可以繼承褐隆,但是一般情況下不會主動去調(diào)用load
方法,都是讓系統(tǒng)自動調(diào)用剖踊。
1. Category中load
方法
// TODO: ----------------- Person類 -----------------
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
+ (void)load {
NSLog(@"Person + load");
}
+ (void)test {
NSLog(@"Person + test");
}
@end
// TODO: ----------------- Person+Category1類 -----------------
@implementation Person (Category1)
+ (void)load {
NSLog(@"Person (Category1) + load");
}
+ (void)test {
NSLog(@"Person (Category1) + test");
}
@end
// TODO: ----------------- Person+Category2類 -----------------
@implementation Person (Category2)
+ (void)load {
NSLog(@"Person (Category2) + load");
}
+ (void)test {
NSLog(@"Person (Category2) + test");
}
@end
// TODO: ----------------- ViewController類 -----------------
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// [Person test];
}
// 打印結(jié)果
Demo[1234:567890] Person + load
Demo[1234:567890] Person (Category1) + load
Demo[1234:567890] Person (Category2) + load
代碼如上:
定義一個Person
類庶弃、定義兩個Person
的分類,然后分別實現(xiàn)load
方法蜜宪、test
方法虫埂,不做任何操作,直接運行程序得出打印結(jié)果圃验。
發(fā)現(xiàn)類和分類的load
方法被調(diào)用掉伏,這是因為:load
方法在Runtime加載類、分類的時候調(diào)用澳窑。
接著放開[Person test]
語句斧散,調(diào)用test
方法,運行程序打印結(jié)果:
// 打印結(jié)果
Demo[1234:567890] Person + load
Demo[1234:567890] Person (Category1) + load
Demo[1234:567890] Person (Category2) + load
Demo[1234:567890] Person (Category2) + test
查看編譯順序:
由iOS底層原理 - 探尋Category本質(zhì)(一)可知:
分類重寫類的方法會優(yōu)先調(diào)用摊聋,類的方法存在最后的內(nèi)存中鸡捐,所以調(diào)用順序優(yōu)先按照分類的編譯順序逆序排列。
結(jié)論跟打印結(jié)果一致麻裁。
2. load
方法調(diào)用原理
接下來根據(jù)OC源碼進行分析箍镜,首先找到Runtime初始化函數(shù)_objc_init
:
然后通過load_images
讀取模塊找到load_images
函數(shù):
然后通過call_load_methods
讀找到call_load_methods
函數(shù):
源碼顯示:
優(yōu)先調(diào)用類的
load
方法,再調(diào)用分類的load
方法煎源。
然后通過call_class_loads
找到call_class_loads
函數(shù):
源碼顯示:
call_class_loads
函數(shù)直接取出類里面的方法色迂,返回方法的地址,通過地址直接調(diào)用load
方法手销。
同理通過call_category_loads
找到call_category_loads
函數(shù):
源碼顯示:
call_category_loads
函數(shù)直接取出分類里面的方法歇僧,返回方法的地址,通過地址直接調(diào)用load
方法锋拖;- 分類中
load
方法的調(diào)用順序是按照編譯順序正序調(diào)用的诈悍。
Q: test
方法調(diào)用方式與load
方法調(diào)用方式有何不同祸轮?
load
方法調(diào)用方式是通過 地址 直接調(diào)用;test
方法調(diào)用方式是通過 消息發(fā)送機制 進行調(diào)用侥钳。
3. load
方法調(diào)用順序
以上分析可知:
load_images
函數(shù)在執(zhí)行call_load_methods();
語句之前适袜,有一個prepare_load_methods
函數(shù)為load
函數(shù)處理:
然后通過prepare_load_methods
找到prepare_load_methods
函數(shù):
優(yōu)先對類進行處理,分類直接按順序存儲慕趴。
然后通過schedule_class_load
找到schedule_class_load
函數(shù)痪蝇,看看需要對類做什么樣的處理:
首先對
schedule_class_load
函數(shù)進行遞歸操作鄙陡,先獲取父類存儲冕房,然后獲取子類存儲。
通過以上分析可知:
load
方法的調(diào)用原理:
load
方法會在Runtime加載類趁矾、分類時調(diào)用耙册;- 每個類、分類的
load
方法在程序運行過程中只調(diào)用一次毫捣。
load
方法的調(diào)用順序:
- 先調(diào)用類的
load
方法详拙;
a> 按照編譯的先后順序調(diào)用;
b> 調(diào)用子類的load
方法之前會先調(diào)用父類的load
方法蔓同;- 再調(diào)用分類的
load
方法饶辙;
a> 按照編譯的先后順序調(diào)用。
接下來通過代碼驗證一下:
// TODO: ----------------- Person類 -----------------
@implementation Person
+ (void)load {
NSLog(@"Person + load");
}
@end
// TODO: ----------------- Person (Category1)類 -----------------
@implementation Person (Category1)
+ (void)load {
NSLog(@"Person (Category1) + load");
}
@end
// TODO: ----------------- Person (Category2)類 -----------------
@implementation Person (Category2)
+ (void)load {
NSLog(@"Person (Category2) + load");
}
@end
// TODO: ----------------- Student類斑粱,繼承Person類 -----------------
@implementation Student
+ (void)load {
NSLog(@"Student + load");
}
@end
// TODO: ----------------- Student (Category1)類 -----------------
@implementation Student (Category1)
+ (void)load {
NSLog(@"Student (Category1) + load");
}
@end
// TODO: ----------------- Student (Category2)類 -----------------
@implementation Student (Category2)
+ (void)load {
NSLog(@"Student (Category2) + load");
}
@end
// TODO: ----------------- Animal類弃揽,獨立的類 -----------------
@implementation Animal
+ (void)load {
NSLog(@"Animal + load");
}
@end
分析以上:
首先調(diào)用類的
load
方法,按照編譯的先后順序調(diào)用则北,則先調(diào)用的是Student
類矿微,但是調(diào)用子類的load
方法之前會先調(diào)用父類的load
方法,所以先調(diào)用Person
類尚揣,再調(diào)用Student
類涌矢,最后調(diào)用Animal
類;再調(diào)用分類的
load
方法快骗,按照編譯的先后順序調(diào)用娜庇,所以調(diào)用順序為:Student (Category1)
、Student (Category2)
方篮、Person (Category1)
名秀、Person (Category2)
。
打印結(jié)果如下:
// 打印結(jié)果
Demo[1234:567890] Person + load
Demo[1234:567890] Student + load
Demo[1234:567890] Animal + load
Demo[1234:567890] Student (Category1) + load
Demo[1234:567890] Student (Category2) + load
Demo[1234:567890] Person (Category1) + load
Demo[1234:567890] Person (Category2) + load
打印結(jié)果與分析結(jié)果一致恭取,結(jié)論可證泰偿。