OC基礎--類的本質

類的本質:編程

  • 類的本質其實也是一個對象(類對象),只要有了類對象, 未來就能夠經過類對象來建立實例對象
  • 程序中第一次使用該類的時候被建立,在整個程序中只有一份。此後每次使用都是這個類對象,它在程序運行時一直存在。
  • 類對象是一種數據結構,存儲類的基本信息:類大小,類名稱,類的版本,繼承層次,以及消息與函數的映射表等
  • 類對象表明類,Class類型,保存了當前對象全部的對象方法,當給一個實例對象發送消息的時候, 會根據實例對象中的isa指針去對應的類對象中查找
  • 若是消息的接收者是類名,則類名錶明類對象
  • 全部類的實例都由類對象生成,類對象會把實例的isa的值修改爲本身的地址,每一個實例的isa都指向該實例的類對象

獲取類對象:緩存

  經過實例對象:數據結構

    格式:[實例對象   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];
相關文章
相關標籤/搜索