Objective-C macro to detect if variable is primitive - objective-c

I'm looking a macro to detect if a variable is an object or a primitive in Objective-C.
In this context, I know the parameter must be a variable and will never be an expression.
Here is the best thing I've come up with:
#define IS_OBJECT(x) ( #encode(__typeof__(x))[0] == '#' )
#define IS_PRIMITIVE(x) ( !IS_OBJECT(x) )
Usage:
NSString *testString = #"test";
NSString *nilString = nil;
NSInteger testInteger = 1;
STAssertTrue(IS_OBJECT(testString), #"IS_OBJECT(testString) must be YES");
STAssertTrue(IS_OBJECT(nilString), #"IS_OBJECT(nilString) must be YES");
STAssertFalse(IS_OBJECT(testInteger), #"IS_OBJECT(testInteger) must be NO");
There must be a better way.
Update
Considering #ChrisDevereux comment, I updated the IS_OBJECT macro.
#define IS_OBJECT(x) ( strchr("##", #encode(__typeof__(x))[0]) != NULL )
It now passes:
NSString *testString = #"test";
NSString *nilString = nil;
NSInteger testInteger = 1;
Class classTest = [NSString class];
STAssertTrue(IS_OBJECT(testString), #"IS_OBJECT(testString) must be YES");
STAssertTrue(IS_OBJECT(nilString), #"IS_OBJECT(nilString) must be YES");
STAssertFalse(IS_OBJECT(testInteger), #"IS_OBJECT(testInteger) must be NO");
STAssertTrue(IS_OBJECT(classTest), #"IS_OBJECT(classTest) must be YES");
I still don't like this answer, and hope there is something slicker. Is there something in the runtime library which does this?

Here's another way using C11's generic selection mechanism. _Generic is standard (modern) C and supported in clang for a while.
#define IS_OBJECT(T) _Generic( (T), id: YES, default: NO)
It feels a bit less runtime-ish to me so I prefer it over the #encode way. But to be honest I just used it for this answer because I love the power _Generic gives to macros and think more people should start using it. If you don't know it you should read Robert Gamble's article linked above.

Related

Objective-C: Passing an IF statement as a argument

I need to pass an IF statement to a method. In JavaScript you can assign a function to a variable. Then that variable can be passed to a function and executed. Does this exist in Objective-C?
This is the pattern I'd like to implement:
-(void)singleComparisonWith:(NSArray *)data
IndexBegin:(NSUInteger)indexBegin
IndexEnd:(NSUInteger)indexEnd
Threshold:(float)threshold {
NSIndexSet *set1 = [self searchWithData:data
Range:[self makeInspectionWithRange:indexBegin
End:indexEnd]
Option:NSEnumerationConcurrent
Comparison:XXXXXXXXX];
// XXXXXXXXX is an IF statement that looks for value at an index above threshold
}
-(void)rangeComparisonWith:(NSArray *)data
IndexBegin:(NSUInteger)indexBegin
IndexEnd:(NSUInteger)indexEnd
ThresholdLow:(float)thresholdLow
ThresholdHigh:(float)thresholdHigh {
NSIndexSet *candidates = [self searchWithData:data
Range:[self makeInspectionWithRange:indexBegin
End:indexEnd]
Option:NSEnumerationReverse
Comparison:YYYYYYYYY];
// YYYYYYYYY is an IF statement that looks for value at an index above thresholdLow and above thresholdHigh
}
-(NSIndexSet *)searchWithData:data
Range:(NSIndexSet *)range
Option:(NSEnumerationOptions)option
Comparison:(id)comparison {
return [data indexesOfObjectsAtIndexes:range
options:option
passingTest:^(id obj, NSUInteger idx, BOOL *stop){
// Comparison is used here. Returns YES if conditions(s) are met.
}
];
}
EDIT:
Here's the solution thanks to #Charles Srstka.
NSIndexSet *set1 = [self searchWithData:data
Range:[self makeInspectionWithRange:indexBegin
End:indexEnd]
Option:NSEnumerationConcurrent
Comparison:BOOL^(id o) {
return ([o floatValue] > threshold);
}
];
-(NSIndexSet *)searchWithData:data
Range:(NSIndexSet *)range
Option:(NSEnumerationOptions)option
Comparison:(BOOL(^)(id o))comparison {
return [data indexesOfObjectsAtIndexes:range
options:option
passingTest:^(id obj, NSUInteger idx, BOOL *stop){
return comparison(obj);
}
];
No errors in that segment.
Thank you for your help.
What you want in Objective-C is called block syntax. While certainly not the nicest thing to look at, or the easiest thing to remember, it will do what you want.
// declares a block named 'foo' (yes, the variable name goes inside the parens)
NSUInteger (^foo)(NSString *) = ^(NSString *baz) {
return [baz length];
};
// now you can call foo like a function:
NSUInteger result = foo(#"hello world");
// or pass it to something else:
[someObject doSomethingWith:foo];
// A method that takes a block looks like this:
- (void)doSomethingWith:(NSUInteger (^)(NSString *))block;
This site is a handy "cheat sheet" that lists all the ways to declare a block in Objective-C. You will probably be referring to it often. The URL I linked to is a newer, work-friendly mirror. I'm sure you can guess the site's original URL if you think about it. ;-)
Basically whenever you see a ^ in Objective-C, you're looking at a block declaration. Unless, of course, you're looking at an XOR operation. But usually it's a block.
EDIT: Look at the site I linked to, where it says "as an argument to a method call." You need to declare it using that syntax, i.e.
... comparison: ^BOOL(id o) {
return ([o floatValue] > threshold);
}];
I know it's not the most intuitive syntax in the world, which is why that site is useful as a cheat sheet.
Also, unrelated to your issue, but Objective-C naming convention is to start the argument labels with lower-case letters; i.e. range:, options:, and comparison: rather than Range:, Option:, Comparison:.

What is the best way to initialise a variable in objective c, before a conditional statement?

I used to initialise variables before a conditional statement in the following way:
NSString *string = [[NSString alloc] init];
if (conditional statement) {
string = #"foo";
}
else{
string = #"bar";
}
But the Xcode Analyser complains:
"Value stored to 'string' during its initialization is never read"
So, I then tried a couple of different options:
A:
NSString *string = nil;
if (conditional statement) {
string = #"foo";
}
else{
string = #"bar";
}
B:
NSString *string = #"bar";
if (conditional statement) {
string = #"foo";
}
So my question is, what is the best way to initialise a variable in objective c, before a conditional statement?
UPDATE:
The variable itself is not used [read] in the conditional. Here is an example below...
NSString *string = [[NSString alloc] init];
if (x == 0) {
string = #"foo";
}
else{
string = #"bar";
}
UPDATE:
Based on Sven's answer, it seems like a good compromise is:
NSString *string;
if (x == 0) {
string = #"foo";
}
else{
string = #"bar";
}
A and B are both valid options. In the end it won't really matter, if you just assign string literals. The compiler might even generate the same code for both cases.
Of course if you assign something other than compile-time constants you need to be more careful. Depending on the side effects that happen in your computation only one or the other version will be correct.
In your case A you won't even have to nil-initialise the variable at first, the compiler is smart enough to see that it is initialised in any case. For patterns like this where you want to initialise an variable depending on some conditions it's actually a good idea to skip the initialisation where the variable is defined. Then the compiler can produce a warning if there is a code path where you forgot to initialise the variable.
Another option for this is to use the ternary operator ?::
NSString *string = condition ? #"foo" : #"bar";
This is not just shorter to write, but also makes it immediately clear that the variable is initialised no matter what the condition is.

Objective c — Update parameter in block

I was doing some tinkering with tree traversals (which I have solved in a much more straightforward way) but I have come across an issue in the following piece of Objective C logic:
- (NSString *)someWrapperFunction
{
NSString *result = #"";
NSString *(^appendBlock)(int, NSString **) = ^NSString *(int a, NSString **adder){
if (a == 0)
{
// base case
return #"";
}
NSLog(#"%d", a);
*adder = [*adder stringByAppendingFormat:#"-%d-", a];
NSLog(#"adder: %#", *adder);
return [*adder stringByAppendingString:appendBlock(a-1, adder)];
};
appendBlock(5, &result);
return result;
}
Basically, I want to create a block of code that concatenates numbers into the given string (adder). The result should be: "-5--4--3--2--1-".
I get a segmentation fault with the above code but with some other code that I wrote for the tree traversal, the adder string was essentially not getting updated. Any pointers to what I am doing wrong here? (Is it possible that the variable that is being updated by the inner block (inside recursion) is disallowed as it is already being occupied by the outer block OR is it just that NSString is non-mutable data type?)
In any case, I want to keep the design of the function the same; how would I solve this problem (using c/objective)?
After some searching and experimenting I found a way to fix this.
There is no reason to be using a double-pointer for your adder parameter in the block. Just use a regular pointer and update your code accordingly.
The error is coming from the fact that inside of the block, appendBlock is NULL and you end up dereferencing the NULL pointer trying to call it.
Here's an updated version that works:
- (NSString *)someWrapperFunction
{
NSString *result = #"";
NSString *(^appendBlock)(int, NSString *);
__block __weak NSString *(^weakBlock)(int, NSString *);
weakBlock = appendBlock = ^NSString *(int a, NSString *adder){
NSString *(^innerBlock)(int, NSString *) = weakBlock;
if (a == 0)
{
// base case
return #"";
}
NSLog(#"%d", a);
adder = [adder stringByAppendingFormat:#"-%d-", a];
NSLog(#"adder: %#", adder);
// Split this update to make it easier to debug.
NSString *update = innerBlock(a-1, adder);
return [adder stringByAppendingString:update];
};
appendBlock(5, result);
return result;
}
Output: "-5--4--3--2--1-"
This update is rewritten for point #1 (which really has nothing to do with your original issue.
To solve point #2 this update creates the original appendBlock variable as well as a new __block __weak weakBlock reference to the same block. And then inside the block, a new (strong) block pointer is created to reference the weak block pointer. Without the use of the weak pointer, the code works but causes a warning.

OBJ C Macro by Stringname

I try to get a macro by NSString value:
Example:
#define FOO1 NSLocalizedString (#"TEXT",#"COMMENT")
#define FOO2 NSLocalizedString (#"TEXT2",#"COMMENT2")
in .m File:
NSString *macroName = [NSString stringWithFormat:"FOO%d",1];
label.text = MACROBYNAME(macroName);
is there a way to do something like this?
Another approach to solution is a Switch-Case in the macro like this:
#define FOOSELECTOR(x) Switch x
case "FOO1"
NSLocalizedString (#"TEXT",#"COMMENT")
break;
case "FOO2"
NSLocalizedString (#"TEXT2",#"COMMENT2")
break;
or something like that... any Ideas?
Okay, I'll bite and make this an answer instead of a comment:
No, it's not possible. There's no way to do what you want to do.
Macros are evaluated during compile time, that's when you hit cmd-b in Xcode. Functions are called during runtime. That's when the program is run, possibly years after compilation.
Furhtermore, why do you want to do it? What's wrong with
NSString *localizedString = NSLocalizedString (#"TEXT",#"COMMENT");
label.text = [NSString stringWithFormat:#"%#%d", localizedString, 1];
?

Objective-C switch using objects?

I'm doing some Objective-C programming that involves parsing an NSXmlDocument and populating an objects properties from the result.
First version looked like this:
if([elementName compare:#"companyName"] == 0)
[character setCorporationName:currentElementText];
else if([elementName compare:#"corporationID"] == 0)
[character setCorporationID:currentElementText];
else if([elementName compare:#"name"] == 0)
...
But I don't like the if-else-if-else pattern this produces. Looking at the switch statement I see that i can only handle ints, chars etc and not objects... so is there a better implementation pattern I'm not aware of?
BTW I did actually come up with a better solution for setting the object's properties, but I want to know specifically about the if-else vs switch pattern in Objective-C
You should take advantage of Key-Value Coding:
[character setValue:currentElementText forKey:elementName];
If the data is untrusted, you might want to check that the key is valid:
if (![validKeysCollection containsObject:elementName])
// Exception or error
I hope you'll all forgive me for going out on a limb here, but I would like to address the more general question of parsing XML documents in Cocoa without the need of if-else statements. The question as originally stated assigns the current element text to an instance variable of the character object. As jmah pointed out, this can be solved using key-value coding. However, in a more complex XML document this might not be possible. Consider for example the following.
<xmlroot>
<corporationID>
<stockSymbol>EXAM</stockSymbol>
<uuid>31337</uuid>
</corporationID>
<companyName>Example Inc.</companyName>
</xmlroot>
There are multiple approaches to dealing with this. Off of the top of my head, I can think of two using NSXMLDocument. The first uses NSXMLElement. It is fairly straightforward and does not involve the if-else issue at all. You simply get the root element and go through its named elements one by one.
NSXMLElement* root = [xmlDocument rootElement];
// Assuming that we only have one of each element.
[character setCorperationName:[[[root elementsForName:#"companyName"] objectAtIndex:0] stringValue]];
NSXMLElement* corperationId = [root elementsForName:#"corporationID"];
[character setCorperationStockSymbol:[[[corperationId elementsForName:#"stockSymbol"] objectAtIndex:0] stringValue]];
[character setCorperationUUID:[[[corperationId elementsForName:#"uuid"] objectAtIndex:0] stringValue]];
The next one uses the more general NSXMLNode, walks through the tree, and directly uses the if-else structure.
// The first line is the same as the last example, because NSXMLElement inherits from NSXMLNode
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
if([[aNode name] isEqualToString:#"companyName"]){
[character setCorperationName:[aNode stringValue]];
}else if([[aNode name] isEqualToString:#"corporationID"]){
NSXMLNode* correctParent = aNode;
while((aNode = [aNode nextNode]) == nil && [aNode parent != correctParent){
if([[aNode name] isEqualToString:#"stockSymbol"]){
[character setCorperationStockSymbol:[aNode stringValue]];
}else if([[aNode name] isEqualToString:#"uuid"]){
[character setCorperationUUID:[aNode stringValue]];
}
}
}
}
This is a good candidate for eliminating the if-else structure, but like the original problem, we can't simply use switch-case here. However, we can still eliminate if-else by using performSelector. The first step is to define the a method for each element.
- (NSNode*)parse_companyName:(NSNode*)aNode
{
[character setCorperationName:[aNode stringValue]];
return aNode;
}
- (NSNode*)parse_corporationID:(NSNode*)aNode
{
NSXMLNode* correctParent = aNode;
while((aNode = [aNode nextNode]) == nil && [aNode parent != correctParent){
[self invokeMethodForNode:aNode prefix:#"parse_corporationID_"];
}
return [aNode previousNode];
}
- (NSNode*)parse_corporationID_stockSymbol:(NSNode*)aNode
{
[character setCorperationStockSymbol:[aNode stringValue]];
return aNode;
}
- (NSNode*)parse_corporationID_uuid:(NSNode*)aNode
{
[character setCorperationUUID:[aNode stringValue]];
return aNode;
}
The magic happens in the invokeMethodForNode:prefix: method. We generate the selector based on the name of the element, and perform that selector with aNode as the only parameter. Presto bango, we've eliminated the need for an if-else statement. Here's the code for that method.
- (NSNode*)invokeMethodForNode:(NSNode*)aNode prefix:(NSString*)aPrefix
{
NSNode* ret = nil;
NSString* methodName = [NSString stringWithFormat:#"%#%#:", prefix, [aNode name]];
SEL selector = NSSelectorFromString(methodName);
if([self respondsToSelector:selector])
ret = [self performSelector:selector withObject:aNode];
return ret;
}
Now, instead of our larger if-else statement (the one that differentiated between companyName and corporationID), we can simply write one line of code
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
aNode = [self invokeMethodForNode:aNode prefix:#"parse_"];
}
Now I apologize if I got any of this wrong, it's been a while since I've written anything with NSXMLDocument, it's late at night and I didn't actually test this code. So if you see anything wrong, please leave a comment or edit this answer.
However, I believe I have just shown how properly-named selectors can be used in Cocoa to completely eliminate if-else statements in cases like this. There are a few gotchas and corner cases. The performSelector: family of methods only takes 0, 1, or 2 argument methods whose arguments and return types are objects, so if the types of the arguments and return type are not objects, or if there are more than two arguments, then you would have to use an NSInvocation to invoke it. You have to make sure that the method names you generate aren't going to call other methods, especially if the target of the call is another object, and this particular method naming scheme won't work on elements with non-alphanumeric characters. You could get around that by escaping the XML element names in your method names somehow, or by building an NSDictionary using the method names as the keys and the selectors as the values. This can get pretty memory intensive and end up taking a longer time. performSelector dispatch like I described is pretty fast. For very large if-else statements, this method may even be faster than an if-else statement.
If you want to use as little code as possible, and your element names and setters are all named so that if elementName is #"foo" then setter is setFoo:, you could do something like:
SEL selector = NSSelectorFromString([NSString stringWithFormat:#"set%#:", [elementName capitalizedString]]);
[character performSelector:selector withObject:currentElementText];
or possibly even:
[character setValue:currentElementText forKey:elementName]; // KVC-style
Though these will of course be a bit slower than using a bunch of if statements.
[Edit: The second option was already mentioned by someone; oops!]
Dare I suggest using a macro?
#define TEST( _name, _method ) \
if ([elementName isEqualToString:# _name] ) \
[character _method:currentElementText]; else
#define ENDTEST { /* empty */ }
TEST( "companyName", setCorporationName )
TEST( "setCorporationID", setCorporationID )
TEST( "name", setName )
:
:
ENDTEST
One way I've done this with NSStrings is by using an NSDictionary and enums. It may not be the most elegant, but I think it makes the code a little more readable. The following pseudocode is extracted from one of my projects:
typedef enum { UNKNOWNRESIDUE, DEOXYADENINE, DEOXYCYTOSINE, DEOXYGUANINE, DEOXYTHYMINE } SLSResidueType;
static NSDictionary *pdbResidueLookupTable;
...
if (pdbResidueLookupTable == nil)
{
pdbResidueLookupTable = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInteger:DEOXYADENINE], #"DA",
[NSNumber numberWithInteger:DEOXYCYTOSINE], #"DC",
[NSNumber numberWithInteger:DEOXYGUANINE], #"DG",
[NSNumber numberWithInteger:DEOXYTHYMINE], #"DT",
nil];
}
SLSResidueType residueIdentifier = [[pdbResidueLookupTable objectForKey:residueType] intValue];
switch (residueIdentifier)
{
case DEOXYADENINE: do something; break;
case DEOXYCYTOSINE: do something; break;
case DEOXYGUANINE: do something; break;
case DEOXYTHYMINE: do something; break;
}
The if-else implementation you have is the right way to do this, since switch won't work with objects. Apart from maybe being a bit harder to read (which is subjective), there is no real downside in using if-else statements this way.
Although there's not necessarily a better way to do something like that for one time use, why use "compare" when you can use "isEqualToString"? That would seem to be more performant since the comparison would halt at the first non-matching character, rather than going through the whole thing to calculate a valid comparison result (though come to think of it the comparison might be clear at the same point) - also though it would look a little cleaner because that call returns a BOOL.
if([elementName isEqualToString:#"companyName"] )
[character setCorporationName:currentElementText];
else if([elementName isEqualToString:#"corporationID"] )
[character setCorporationID:currentElementText];
else if([elementName isEqualToString:#"name"] )
There is actually a fairly simple way to deal with cascading if-else statements in a language like Objective-C. Yes, you can use subclassing and overriding, creating a group of subclasses that implement the same method differently, invoking the correct implementation at runtime using a common message. This works well if you wish to choose one of a few implementations, but it can result in a needless proliferation of subclasses if you have many small, slightly different implementations like you tend to have in long if-else or switch statements.
Instead, factor out the body of each if/else-if clause into its own method, all in the same class. Name the messages that invoke them in a similar fashion. Now create an NSArray containing the selectors of those messages (obtained using #selector()). Coerce the string you were testing in the conditionals into a selector using NSSelectorFromString() (you may need to concatenate additional words or colons to it first depending on how you named those messages, and whether or not they take arguments). Now have self perform the selector using performSelector:.
This approach has the downside that it can clutter-up the class with many new messages, but it's probably better to clutter-up a single class than the entire class hierarchy with new subclasses.
Posting this as a response to Wevah's answer above -- I would've edited, but I don't have high enough reputation yet:
unfortunately the first method breaks for fields with more than one word in them -- like xPosition. capitalizedString will convert that to Xposition, which when combined with the format give you setXposition: . Definitely not what was wanted here. Here is what I'm using in my code:
NSString *capName = [elementName stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[[elementName substringToIndex:1] uppercaseString]];
SEL selector = NSSelectorFromString([NSString stringWithFormat:#"set%#:", capName]);
Not as pretty as the first method, but it works.
I have come up with a solution that uses blocks to create a switch-like structure for objects. There it goes:
BOOL switch_object(id aObject, ...)
{
va_list args;
va_start(args, aObject);
id value = nil;
BOOL matchFound = NO;
while ( (value = va_arg(args,id)) )
{
void (^block)(void) = va_arg(args,id);
if ( [aObject isEqual:value] )
{
block();
matchFound = YES;
break;
}
}
va_end(args);
return matchFound;
}
As you can see, this is an oldschool C function with variable argument list. I pass the object to be tested in the first argument, followed by the case_value-case_block pairs. (Recall that Objective-C blocks are just objects.) The while loop keeps extracting these pairs until the object value is matched or there are no cases left (see notes below).
Usage:
NSString* str = #"stuff";
switch_object(str,
#"blah", ^{
NSLog(#"blah");
},
#"foobar", ^{
NSLog(#"foobar");
},
#"stuff", ^{
NSLog(#"stuff");
},
#"poing", ^{
NSLog(#"poing");
},
nil); // <-- sentinel
// will print "stuff"
Notes:
this is a first approximation without any error checking
the fact that the case handlers are blocks, requires additional care when it comes to visibility, scope and memory management of variables referenced from within
if you forget the sentinel, you are doomed :P
you can use the boolean return value to trigger a "default" case when none of the cases have been matched
The most common refactoring suggested for eliminating if-else or switch statements is introducing polymorphism (see http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html). Eliminating such conditionals is most important when they are duplicated. In the case of XML parsing like your sample you are essentially moving the data to a more natural structure so that you won't have to duplicate the conditional elsewhere. In this case the if-else or switch statement is probably good enough.
In this case, I'm not sure if you can easily refactor the class to introduce polymorphism as Bradley suggests, since it's a Cocoa-native class. Instead, the Objective-C way to do it is to use a class category to add an elementNameCode method to NSSting:
typedef enum {
companyName = 0,
companyID,
...,
Unknown
} ElementCode;
#interface NSString (ElementNameCodeAdditions)
- (ElementCode)elementNameCode;
#end
#implementation NSString (ElementNameCodeAdditions)
- (ElementCode)elementNameCode {
if([self compare:#"companyName"]==0) {
return companyName;
} else if([self compare:#"companyID"]==0) {
return companyID;
} ... {
}
return Unknown;
}
#end
In your code, you could now use a switch on [elementName elementNameCode] (and gain the associated compiler warnings if you forget to test for one of the enum members etc.).
As Bradley points out, this may not be worth it if the logic is only used in one place.
What we've done in our projects where we need to so this sort of thing over and over, is to set up a static CFDictionary mapping the strings/objects to check against to a simple integer value. It leads to code that looks like this:
static CFDictionaryRef map = NULL;
int count = 3;
const void *keys[count] = { #"key1", #"key2", #"key3" };
const void *values[count] = { (uintptr_t)1, (uintptr_t)2, (uintptr_t)3 };
if (map == NULL)
map = CFDictionaryCreate(NULL,keys,values,count,&kCFTypeDictionaryKeyCallBacks,NULL);
switch((uintptr_t)CFDictionaryGetValue(map,[node name]))
{
case 1:
// do something
break;
case 2:
// do something else
break;
case 3:
// this other thing too
break;
}
If you're targeting Leopard only, you could use an NSMapTable instead of a CFDictionary.
Similar to Lvsti I am using blocks to perform a switching pattern on objects.
I wrote a very simple filter block based chain, that takes n filter blocks and performs each filter on the object.
Each filter can alter the object, but must return it. No matter what.
NSObject+Functional.h
#import <Foundation/Foundation.h>
typedef id(^FilterBlock)(id element, NSUInteger idx, BOOL *stop);
#interface NSObject (Functional)
-(id)processByPerformingFilterBlocks:(NSArray *)filterBlocks;
#end
NSObject+Functional.m
#implementation NSObject (Functional)
-(id)processByPerformingFilterBlocks:(NSArray *)filterBlocks
{
__block id blockSelf = self;
[filterBlocks enumerateObjectsUsingBlock:^( id (^block)(id,NSUInteger idx, BOOL*) , NSUInteger idx, BOOL *stop) {
blockSelf = block(blockSelf, idx, stop);
}];
return blockSelf;
}
#end
Now we can set up n FilterBlocks to test for the different cases.
FilterBlock caseYES = ^id(id element, NSUInteger idx, BOOL *breakAfter){
if ([element isEqualToString:#"YES"]) {
NSLog(#"You did it");
*breakAfter = YES;
}
return element;
};
FilterBlock caseNO = ^id(id element, NSUInteger idx, BOOL *breakAfter){
if ([element isEqualToString:#"NO"] ) {
NSLog(#"Nope");
*breakAfter = YES;
}
return element;
};
Now we stick those block we want to test as a filter chain in an array:
NSArray *filters = #[caseYES, caseNO];
and can perform it on an object
id obj1 = #"YES";
id obj2 = #"NO";
[obj1 processByPerformingFilterBlocks:filters];
[obj2 processByPerformingFilterBlocks:filters];
This approach can be used for switching but also for any (conditional) filter chain application, as the blocks can edit the element and pass it on.