類的本質:編程
獲取類對象:緩存
經過實例對象:數據結構
格式:[實例對象 class ];
如: [dog class];函數
經過類名獲取(類名其實就是類對象)性能
格式:[類名 class];
如:[Dog class]spa
類對象的用法:設計
用來調用類方法:指針
[Dog test];code
Class c = [Dog class];
[c test];orm
用來建立實例對象:
Dog *g = [Dog new];
Class c = [Dog class];
Dog *g1 = [c new];
類對象的存儲:
OC實例對象 類對象 元對象:
OC對象:
1 每個對象都是一個類的實例。
2 每個對象都有一個名爲isa的指針,指向該對象的類。
3 每個類描述了一系列它的實例的特色,包括成員變量的列表,成員函數的列表等。
4 每個類實際上也是一個對象。每個類也有一個名爲isa的指針。每個類均可以接受消息,例如[NSObject new],就是向NSObject這個類發送名爲new的消息。
元類:
由於類也是一個對象,那它也必須是另外一個類的實例,這個類就是元類 (metaclass)
1 元類保存了類方法的列表。當一個類方法被調用時,元類會首先查找它自己是否有該類方法的實現,若是沒有則該元類會向它的父類查找該方法,直到一直找到繼承鏈的頭。
2 元類(metaclass)也是一個對象,也有isa指針,全部的元類的isa指針都會指向一個根元類(root metaclass)。
3 根元類(root metaclass)自己的isa指針指向本身,這樣就行成了一個閉環。一個對象可以接收的消息列表是保存在它所對應的類中的。在實際編程 中,咱們幾乎不會遇到向元類發消息的狀況,那它的isa 指針在實際上不多用到。不過這麼設計保證了面向對象的乾淨,即全部事物都是對象,都有isa指針。
4 因爲類方法的定義是保存在元類(metaclass)中,而方法調用的規則是,若是該類沒有一個方法的實現,則向它的父類繼續查找。因此爲了保證父類的類方法能夠在子類中能夠被調用,因此子類的元類會繼承父類的元類,換而言之,類對象和元類對象有着一樣的繼承關係。
假設: Person類有一個對象方法test 和 一個類方法 demo
Student類有一個對象方法test1 和一個類方法 demo1
在實現中,Root Class是指 NSObject
NSObject類對象包括它的對象實例方法。
NSObject的元對象包括它的類方法,例如new方法。
NSObject的元對象繼承自NSObject類。
一個NSObject的類中的方法同時也會被NSObject的子類在查找方法時找到。
類的啓動過程:
+load方法:
1 在程序啓動的時候會加載全部的類和分類,並調用全部類和分類的+load方法(只會調用一次),將全部類的代碼加載到內存中, 放到代碼區
2 先加載父類,再加載子類;也就是先調用父類的+load,再調用子類的+load
3 先加載原始類,再加載分類
4 無論程序運行過程有沒有用到這個類,都會調用+load加載
@implementation Person + (void)load { NSLog(@"%s", __func__); } @end @implementation Student : Person + (void)load { NSLog(@"%s", __func__); } @end 輸出結果: +[Person load] +[Student load]
+initialize方法:
1 在第一次使用某個類時(好比建立對象等),會調用一次+initialize方法, 不管使用多少次這個類都只會調用一次
2 initialize用於對某一個類進行一次性的初始化
3 一個類只會調用一次+initialize方法,先調用父類的,再調用子類的
@implementation Person + (void)initialize { NSLog(@"%s", __func__); } @end @implementation Student : Person + (void)initialize { NSLog(@"%s", __func__); } @end int main(int argc, const char * argv[]) { Student *stu = [Student new]; return 0; } 輸出結果: +[Person initialize] +[Student initialize]
SEL類型:
概念:
1 SEL類型表明着方法的簽名,在類對象的方法列表中存儲着該簽名與方法代碼的對應關係
2 每一個類的方法列表都存儲在類對象中, 每一個方法都有一個與之對應的SEL類型的對象, 根據一個SEL對象就能夠找到方法的地址,進而調用方法
3 SEL類型的定義 typedef struct objc_selector *SEL;
-test方法的調用:
1 首先把test這個方法名包裝成sel類型的數據
2 根據SEL數據到該類的類對象中,去找對應的方法的代碼,若是找到了就執行該代碼
3 若是沒有找到根據類對象上的父類的類對象指針,去父類的類對象中查找,若是找到了,則執行父類的代碼
4 若是沒有找到,一直像上找,直到基類(NSObject), 若是都沒有找到就報錯。
在這個操做過程當中有緩存,第一次找的時候是一個一個的找,很是耗性能,以後再用到的時候就直接使用。
用途:
1 配合對象/類來檢查對象/類中有沒有實現某一個方法
SEL sel = @selector(setAge:); Person *p = [Person new]; // 判斷p對象中有沒有實現-號開頭的setAge:方法 // 若是P對象實現了setAge:方法那麼就會返回YES // 若是P對象沒有實現setAge:方法那麼就會返回NO BOOL flag = [p respondsToSelector:sel]; NSLog(@"flag = %i", flag); // respondsToSelector注意點: 若是是經過一個對象來調用該方法那麼會判斷該對象有沒有實現-號開頭的方法 SEL sel1 = @selector(test); flag = [p respondsToSelector:sel1]; NSLog(@"flag = %i", flag); // 若是是經過類來調用該方法, 那麼會判斷該類有沒有實現+號開頭的方法 flag = [Person respondsToSelector:sel1]; NSLog(@"flag = %i", flag);
2 配合對象/類來調用某一個SEL方法
SEL sel = @selector(test); Person *p = [Person new]; // 調用p對象中sel類型對應的方法 [p performSelector:sel]; // withObject: 須要傳遞的參數 // 注意: 若是經過performSelector調用有參數的方法, 那麼參數必須是對象類型,由於withObject只能傳遞一個對象 SEL sel2 = @selector(setAge:); [p performSelector:sel2 withObject:@(5)]; NSLog(@"age = %i", p.age); // 注意:performSelector最多隻能傳遞2個參數 SEL sel3 = @selector(sendMessageWithNumber:andContent:); [p performSelector:sel3 withObject:@"138383438" withObject:@"abcdefg"];
3 配合對象將SEL類型做爲方法的形參
// 調用傳入對象的指定方法 - (void)makeObject:(id)obj andSel:(SEL)sel; - (void)makeObject:(id)obj andSel:(SEL)sel { [obj performSelector:sel]; } Car *c = [Car new]; SEL sel = @selector(run); Person *p = [Person new]; [p makeObject:c andSel:sel];