1. 程式人生 > >Objective-C中的反射和反射工廠

Objective-C中的反射和反射工廠


  程式中可能會出現大量的if-else或者switch-case來選擇一系列類中的某個類進行操作,利用反射(Reflection)就可以避免這些重複判斷工作。

  反射在Java中封裝了類來實現,在Objective-C裡面要自己來實現,好在不是很難。

  我們的目的是通過傳入一個類名字串,把字串類名動態轉化成類,並對類進行例項化和呼叫方法等操作。

  OC的NSRuntime中提供了將Class name轉化為Class的方法"NSClassFromString()"

NSClassFromString //方法原型
Class NSClassFromString (
   NSString 
*aClassName );

   因此,我們就可以這樣使用來動態獲得一個類:

NSString * aClassName = @"SunnyClass";
Class class = NSClassFromString(aClassName);

  這樣我們就能例項化這個類了:

id instance = [[class alloc] init];

  如果需要使用方法,最好用reponseToSelector:來檢測一下

if([instance reponseToSelector:@selector(aMethod)])
{
    [instance aMethod];
}

  這樣就完成了反射呼叫的過程。

  下面就是將反射利用到工廠模式裡。

  定義一個Fruit,和三種繼承自Fruit的水果

複製程式碼
@interface Fruit : NSObject
- (void)show;
@end

@interface Apple : Fruit 
@end

@interface Banana : Fruit 
@end

@interface Pear : Fruit 
@end
複製程式碼

  實現部分,簡單的show方法來列印

複製程式碼
@implementation Fruit
- (void)show
{
    NSLog(@"I'm a fruit
"); } @end @implementation Apple - (void)show { NSLog(@"I'm an apple"); } @end @implementation Banana - (void)show { NSLog(@"I'm a banana"); } @end @implementation Pear - (void)show { NSLog(@"I'm a pear"); } @end
複製程式碼

  如果使用傳統的if-else分支判斷的話,情況就會變得非常噁心,如果想再增加一個Orange類的話,不僅需要增加Orange類的程式碼,工廠的程式碼也要新增加else-if分支:

複製程式碼
- (Fruit *)createFruitWithName:(NSString *)fruitName
{
    Fruit * theFruit;
    if ([fruitName isEqualToString:@"Apple"]) 
    {
        theFruit = [[[Apple alloc] init] autorelease];
    }
    else if ([fruitName isEqualToString:@"Banana"])
    {
        theFruit = [[[Banana alloc] init] autorelease];
    }
    else if ([fruitName isEqualToString:@"Pear"])
    {
        theFruit = [[[Pear alloc] init] autorelease];
    }
    return theFruit;
}
複製程式碼

  但如果使用反射工廠的模式,情況就變得簡單了許多,擴充套件性也會變得更好(反射輸入的類名最好能來自於一個檔案,這個檔案規定了哪些字串是可以作為引數傳入的,這裡就不考慮了)

複製程式碼
@implementation FruitFactroy

- (Fruit *)createFruitWithName:(NSString *)fruitName
{
    //通過string得到類的結構體
    Class class = NSClassFromString(fruitName);
    
    //通過轉化的class得到例項物件
    Fruit * theFruit = [(Fruit *)[[class alloc] init] autorelease];
    
    //呼叫物件方法
    if ([theFruit respondsToSelector:@selector(show)]) 
    {
        [theFruit show];
    }
    
    //返回物件
    return theFruit;
}
@end
複製程式碼

  工廠方法的呼叫

    FruitFactroy * factroy = [[[FruitFactroy alloc] init] autorelease];
    Fruit * apple = [factroy createFruitWithName:@"Apple"];
    Fruit * banana = [factroy createFruitWithName:@"Banana"];
    Fruit * pear = [factroy createFruitWithName:@"Pear"];