Object C學習筆記17-動態判斷和選擇器

賀臣發表於2014-03-22

  當時學習Object C的時被人鄙視了一頓,說使用.NET的思想來學Object C就是狗屎;不過也挺感謝這位仁兄的,這讓我學習的時候更加的謹慎。今天的學習筆記主要記錄Object C中的動態型別相關內容。

  首先還是和.NET先對比一下,.NET中存在一個關鍵字var ,這個估計用過.NET的都知道,除非沒有接觸過3.0以上版本的。在.NET中能夠使用var來宣告任何型別的區域性變數,負責告訴編譯器,該變數需要根據初始化表示式來推斷變數的型別,而且只能是區域性變數.但是這裡要注意的時var 並不是一個新的型別,只是一個關鍵字而已,在定義引數和返回值的時候都不能使用var.

  在.net中還有一個型別dynamic,表示變數的型別在執行時決定,也就是在執行此程式碼的時候才去校驗型別是否符合等,如果型別不符則會丟擲異常。而var則會在編譯的過程就能夠發現型別是否準確。

  在這裡我們要說Object C中的id型別 和 var 以及 dynamic 有什麼區別?

 

  一. Object C中的id型別

    在Objective-C 中,id 型別是一個獨特的資料型別。在概念上可以轉換為任何資料型別。換句話說,id 型別的變數可以存放任何資料型別的物件。在內部處理上,這種型別被定義為指向物件的指標,實際上是一個指向這種物件的例項變數的指標。

    先看一段程式碼瞭解一下id型別的時候:

id stu=[[Student alloc] init];
[stu eat];
        
id per=[[Person alloc] init];
[per eat];

    以上程式碼分別用建立了兩個類Student 和 Person的例項,型別都是使用的id,並且呼叫了其相應的方法,成功執行。

 

  二. 動態判斷

    在Object C中如何判斷某個類例項是否是某個類的子類,某個類中是否包含某個方法。這個要求似乎和.NET中的反射有點類似,的確在Object中同樣提供了類似的方法來動態判斷。

    我們先定義一個父類:

#import <Foundation/Foundation.h>

@interface Person : NSObject{
    NSString *name;
    int age;
    NSString *address;
}
@property (nonatomic,retain) NSString *name;
@property (nonatomic,assign) int age;
@property (nonatomic,retain) NSString *address;

-(void) eat;

-(void) speak;

-(void) write;

-(Person *) getPerson;

-(id) getString;

+(void) Info;

@end

----------------------------------------------------------------
#import "Person.h"

@implementation Person

@synthesize name;
@synthesize age;
@synthesize address;

-(void) eat{
    NSLog(@"Person.eat()");
}

-(void) speak{
    NSLog(@"Person.speak()");
}

-(void) write{
    NSLog(@"Person.write()");
}

-(Person *) getPerson{
    Person *p=[[Person alloc] init];
    p.name=@"li";
    p.age=25;
    p.address=@"上海";
    return p;
}

-(id) getString{
    Person *p=[[Person alloc] init];
    p.name=@"lin";
    p.age=22;
    p.address=@"北京";
    return p;
}

+(void) Info{
    NSLog(@"Person.Info");
}

@end
Person 類

    然後定義一個子類,用於繼承Person類。

#import "Student.h"

@implementation Student

@end
Student 類

    Student類繼承了Person類,說明Student類擁有了Person類的所有方法和屬性

    (1) isMemberOfClass 用於判斷是否是某個類的例項

bool flag1=[stu isMemberOfClass:[Student class]];
NSLog(@"%d",flag1);
bool flag2=[stu isMemberOfClass:[Person class]];
NSLog(@"%d",flag2);

    上面的程式碼 flag1 為yes,而flag2為no,說明stu是Student類的例項,Student為Person的子類,但是不能判斷為Person的例項。

    (2) isKindOfClass 判斷是否為某個類的例項或者某個類子類的例項

bool flag1=[stu isKindOfClass:[Student class]];
NSLog(@"%d",flag1);
bool flag2=[stu isKindOfClass:[Person class]];
NSLog(@"%d",flag2);

    因為Student是Person的子類,所以上面flag1,flag2 的都為YES

    (3) respondsToSelector 用於判斷某個型別或者物件是否有能力迴應(呼叫)指定的方法

bool flag3=[per respondsToSelector:@selector(eat)];
        NSLog(@"%d",flag3);
        
        bool flag4=[stu respondsToSelector:@selector(eat)];
        NSLog(@"%d",flag4);
        
        bool flag5=[per respondsToSelector:@selector(Info)];
        NSLog(@"%d",flag5);
        
        bool flag6=[Person respondsToSelector:@selector(Info)];
        NSLog(@"%d",flag6);
respondsToSelector 測試程式碼

    上面的程式碼測試輸出結果如下:

2014-03-22 20:28:09.778 ObjectSelect[11321:303] 1
2014-03-22 20:28:09.778 ObjectSelect[11321:303] 1
2014-03-22 20:28:09.778 ObjectSelect[11321:303] 0
2014-03-22 20:28:09.779 ObjectSelect[11321:303] 1

    第一個和第二個方法的呼叫都返回1,說明其有能力迴應(呼叫)方法eat, 關鍵看第三個和第四個方法的呼叫,返回0,1 說明例項和物件的呼叫是不一樣的,而Info方法是一個全域性方法 ,用 "+" 標識的方法就是全域性方法,相當於靜態方法。第3個方法是使用例項來呼叫的返回0,說明物件例項是沒有能力呼叫靜態方法的。

     (4) instancesRespondToSelector 用於判斷某個物件的例項是否有能力迴應(呼叫)指定的方法

        bool flag7=[Person instancesRespondToSelector:@selector(eat)];
        NSLog(@"%d",flag7);
        bool flag8=[Person instancesRespondToSelector:@selector(Info)];
        NSLog(@"%d",flag8);

    上面的測試程式碼輸出結果如下:

2014-03-22 20:39:04.700 ObjectSelect[11344:303] 1
2014-03-22 20:39:04.700 ObjectSelect[11344:303] 0

    第一個方法呼叫返回1,說明Person的例項可以呼叫eat方法,第二個方法返回0,說明Person的例項不可以呼叫Info方法。

 

  三. 如何動態呼叫方法

    上面已經說到了,任何一個物件的例項我們都可以使用id來指向其地址,這是一個動態的型別那麼如何呼叫其具體的方法。

[stu performSelector:@selector(eat)];
[Person performSelector:@selector(Info)];

    使用performSelector 方法可以動態的呼叫其方法,如果是例項方法則呼叫的需要使用物件例項,而呼叫靜態方法這需要使用物件本身。

    上面的辦法呼叫都是沒有引數的,那麼如何呼叫有輸入引數的方法呢?

[stu performSelector:@selector(speak:) withObject:@"ddddd"];
        
[stu performSelector:@selector(write:andAge:) withObject:@"fasdfadaf" withObject:@"333"];

    使用withObject 來填充引數.但是這個方法的呼叫有點不足的地方,如果有多個引數輸入引數就有問題。 這個地方還沒有完全沒有弄清楚,有待後續研究。如果真的涉及到多個引數,那麼可以使用物件封裝好這些引數或者將引數存入一個集合中傳遞輸入。

 

  四. 總結

    這篇主要講到了動態型別id,以及其如何使用。還有關於類的動態判斷以及選擇器。但都只是比較粗略的介紹了,後面的學習中繼續深入研究。

    本文開頭的時候對比了.NET 中的var , dynamic 。 從上面的文章介紹,那麼Object C中的id 和 .NET中的哪個關鍵字比較像,個人的學習體會和dynamic比較類似,如有不對的正確的地方請高手斧正!

 

  補充說明文章: Object C學習筆記18-SEL,@ selector,Class,@class

  本文到此結束,請高手勿噴,菜鳥的學習需要鼓勵!

相關文章