Objective-C中的isa、class、SEL、IMP

在objective c中,若是細心的話會發現,每一個類中都會自動生成一個class 類型的isa,函數

@interface NSObject <NSObject> {  
     Class    isa;  
 }

isa是什麼,class又是什麼呢,找到Class的定義咱們會發現以下:spa

typedefstruct objc_class *Class;


objc_class之前的定義又以下,如今聽說被封閉了,不知道有沒有再做修改,總之方便咱們理解就好:指針

struct objc_class {  
     Class isa;  
       
     Class super_class;     
     const char *name;  
     long version;  
     long info;    
     long instance_size;  
     struct objc_ivar_list *ivars;  
     struct objc_method_list **methodLists;   
     struct objc_cache *cache;  
     struct objc_protocol_list *protocols;     
 }

    因而咱們就有了點頭緒了,isa,is a pointer,是個指針,每一個類都有一個class類型的指針isa,繼承自NSObject中,繼承關係,方法變量等信息都存放在isa中,isa做爲一個隱藏的屬性,會自動生成於每一個類之中。有了這個前提,也就能夠解釋爲何咱們能夠根據@class來代替任意一個類了,看代碼:code

Human.horm

#import <Foundation/Foundation.h>  
  
@interface Human : NSObject  
-(void)say;  
@end

Human.m對象

#import "Human.h"  
  
@implementation Human  
-(void)say  
{  
    NSLog(@"Human中的say方法");  
}  
@end

main.h繼承

#import <Foundation/Foundation.h>  
#import "Human.h"  
int main(int argc, const char * argv[])  
{  
  
    @autoreleasepool {  
          
        Class c =NSClassFromString(@"Human");  
        [[c new] say];  
        //以上CLASS類型的c,就至關於Human類。  
    }  
    return 0;  
}


    class能夠靈活的代替別的類,SEL與其相似,不一樣的是SEL代替的是方法,能夠方便的代替其餘方法,class中是由於有isa屬性保存有類的信息,而SEL是由於即便是在不一樣的類中,方法名只要相同,這兩個方法的ID就相同,SEL就是根據這個ID來找到該方法,再根據調用該方法的類的不一樣來找到惟一的地址。字符串

typedef struct objc_object *id;

typedef struct objc_selector *SEL;

#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id (*IMP)(id, SEL, ...); 
#endif

    從上面的頭文件中咱們能夠看到,IMP定義爲 id (*IMP) (id, SEL, …)。這樣說來, IMP是一個指向函數的指針,這個被指向的函數包括id(「self」指針),調用的SEL(方法名),再加上一些其餘參數。 IMP 就是一個函數指針,這個被指向的函數包含一個接收消息的對象id(self  指針), 調用方法的選標 SEL (方法名),以及不定個數的方法參數,並返回一個id。也就是說 IMP 是消息最終調用的執行代碼,是方法真正的實現代碼 。io

 #if !OBJC_OLD_DISPATCH_PROTOTYPES
OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ )
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
#else


Methodform

typedef struct objc_method *Method;
typedef struct objc_method *Method;
struct objc_method {
  SEL method_name;
  char *method_types;
  IMP method_imp;
};

    這個定義看上去包括了咱們上面說過的其餘類型。也就是說,Method(咱們常說的方法)表示一種類型,這種類型與selector和實現(implementation)相關。


看代碼再做解釋:

#import <Foundation/Foundation.h>  
  
@interface Human : NSObject  
-(void)say;  
@end  
@implementation Human  
-(void)say  
{  
    NSLog(@"Human中的say方法");  
}  
@end

//上面定義了一個human類,裏面有一個say方法  

#import <Foundation/Foundation.h>  

@interface man:NSObject  
{}  
-(void)say; @end  
  
@implementation man  
-(void)say  
{  
    NSLog(@"man中的say方法");  
}  
@end

//在上面定義了一個man類,一樣有一個say方法  

int main(int argc, const char * argv[])  
{  
  
    @autoreleasepool {  
          
        Class a =NSClassFromString(@"Human");  
        Class b =NSClassFromString(@"man");  
        //根據方法名say找到該方法的id,將sel與其綁定;  
        SEL sel = NSSelectorFromString(@"say");  
        [[a new] performSelector:sel];  
        [[b new] performSelector:sel];     
    }  
    return 0;  
}

結果以下:

2012-03-13 10:13:24.900 String[2725:403] Human中的say方法  
2012-03-13 10:13:24.901 String[2725:403] man中的say方法


    經過以上代碼咱們會發現,SEL經過方法名綁定後,能夠被多個類實例調用,找了些網上的資料,解釋都是說方法名同樣的話,ID會同樣,地址仍不一樣,纔會實現這樣的效果,咱們不談論是否準確,但我我的認爲這是目前最合理的解釋。這種用法的優點一方面是靈活性更高,相似於多態,另外一方面是,這種用法sel找方法時匹配的是ID而不是字符串方法名,因此在效率上會高一些。還有一種更終極的方法,直接對應方法的地址,這種方法效率最高,請看代碼:

#import <Foundation/Foundation.h>  
  
@interface Human : NSObject  
-(void)say;  
@end  
@implementation Human  
-(void)say  
{  
    NSLog(@"Human中的say方法");  
}  
@end

//上面定義了一個human類,裏面有一個say方法  

#import <Foundation/Foundation.h>  

@interface man:NSObject  
{}  
-(void)say; @end  
  
@implementation man  
-(void)say  
{  
    NSLog(@"man中的say方法");  
}  
@end

//在上面定義了一個man類,一樣有一個say方法  

int main(int argc, const char * argv[])  
{  
  
    @autoreleasepool {  
          
        Human *human =[Human new];  
        man *ma=[man new];  
        //根據方法名say找到該方法的id,將sel與其綁定;  
        SEL sel =@selector(say);//也能夠這樣寫:SEL sel=NSSelectorFromString(@"say");  
        IMP imp1 = [human methodForSelector:sel];       
        IMP imp2 = [ma methodForSelector:sel];       
  
        imp1(human,sel);  
        imp2(ma,sel);  
        //由於每一個方法都有本身的地址,這種方式直接找到地址區分相同ID的方法,效率最高,但靈活性不如SEL方式。  
          
    }  
    return 0;  
}

輸出語句:

2012-03-13 10:35:21.446 String[3763:403] Human中的say方法  
2012-03-13 10:35:21.450 String[3763:403] man中的say方法


    今天這些內容不太好理解,我用本身理解的方式給你們再解釋一遍,class用於代替類,增長靈活性,由於咱們不知道何時會用到什麼類,方法也是如此,因此SEL能夠代替方法,每一個方法有方法名,ID,地址,相同的方法名,ID也同樣,正常狀況下咱們根據方法名找到方法,用SEL方法能夠根據ID找到方法,而用IMP方式能夠直接找到地址,可是靈活性不如SEL方法,雖然效率最高。

相關文章
相關標籤/搜索