Objective-C中的反射和反射工廠
阿新 • • 發佈:2019-01-01
程式中可能會出現大量的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"];