Function injection in objective-c
I have a random number generator (R开发者_C百科NG) function.
-(double) generateRandomNumber
{
... do some stuff
return randomNumber;
}
I have a function that calls this random number generator function to do a task - something along the lines of
-(double) GenerateSellPrice {
double randomNumber;
randomNumber = [self generateRandomNumber];
return value*randomNumber;
}
I want to modify this so that the random number generator function is actually passed into the GenerateSellPrice function, so that I can swap in and out different RNGs for different purposes (i.e. unit testing, different ranges, etc).
How do I declare the RNG function so that it is in a form that I can pass it around (I'm assuming as a function pointer)? How do I declare the GenerateSellPrice to accept the function as a parameter? And how do I then invoke the function?
You could declare your PRNGs as Objective-C blocks and pass them around like any other object. http://www.mikeash.com/pyblog/friday-qa-2008-12-26.html has some info on the subject.
The classic Objective-C pattern would be to take a selector (SEL
) and object (id
) argument argument:
-(double) GenerateSellPriceWithRNG:(id)rngObject selector:(SEL)rngSelector
double randomNumber;
randomNumber = [rngObject performSelector:rngSelector];
return value*randomNumber;
}
and call it with
[self GenerateSellPrinceWithRNG:self selector:@selector(generateRandomNumber)];
Modern practice would probably take a block (double (^)()
) parameter. The syntax for a block is slightly uglier, but the power it brings is useful in this scenario. You get better type-checking from the compiler and you can easily generate a block inline where convenient rather than having to write an entire class & method.
In this case you could declare
-(double) GenerateSellPriceWithRNG:(double (^)())rngBlock {
double randomNumber;
randomNumber = rngBlock();
return value*randomNumber;
}
and call it with
[self GenerateSellPriceWithRNG:^{ return [self generateRandomNumber]; }];
or with any other block that returns a double
.
The easiest way to do that is to use the "selector" object. You can declare a selector by doing the following
@selector(generateRandomNumber)
Once you have a selector object, you use
SEL randomNumberFunction; // Most likely passed in as a parameter
NSObject *generatorObject;
[generatorObject performSelector:randomNumberFunction];
Worked it out using blocks. For those who want to know how I did it, I declared my RNG in my calling class (classA) as a class variable
double(^generateRandomNumber)(void) = ^{
...do stuff;
return randomNumber;
};
Then declared my function in my called class (classB)
-(double) generateSellPriceWithGenerator:(double(^)(void))generator {
double randomNumber;
randomNumber = generator();
return value*randomNumber;
}
And then simply called the function from classA like so
double (^func)(void) = generateRandomNumber;
double value = [classB generateSellPriceWithGenerator:func];
精彩评论