"Still I have yet to find a simple implementation that I like and that does not use private methods. The last thing I want is a relying on classes which can break at any time."Mea culpa.
While I did explain a bit why the current HOM implementation is a bit gnarly, code probably speaks more loudly than repeated mea-culpas.
So, without further ado, a really simple HOM implementation. An NSArray category provides the interface and does the actual processing:
The fact that NSInvocation deals with pointers to values rather than values makes this a bit longer than it needs to be, but the gist is simple enough: iterate over the array, invoke the invocation, return the result.@interface NSArray(hom) -collect; @end @implementation NSArray(hom) -(NSArray* )collect:(NSInvocation*)anInvocation { NSMutableArray *resultArray=[NSMutableArray array]; for (id obj in self ) { id resultObject; [anInvocation invokeWithTarget:obj]; [anInvocation getReturnValue:&resultObject]; [resultArray addObject:resultObject]; } return resultArray; } -collect { return [HOM homWithTarget:self selector:@selector(collect:)]; } @end
That leaves the actual trampoline, which is really just an implementation detail for conveniently creating NSInvocation objects.
This code compiles without warnings, does not use any private API, and runs on both Leopard and the iPhone. Github: https://github.com/mpw/HOM/.@interface HOM : NSProxy { id xxTarget; SEL xxSelector; } @end @implementation HOM -(void)forwardInvocation:(NSInvocation*)anInvocation { [xxTarget performSelector:xxSelector withObject:anInvocation]; } -methodSignatureForSelector:(SEL)aSelector { return [[xxTarget objectAtIndex:0] methodSignatureForSelector:aSelector]; } -xxinitWithTarget:aTarget selector:(SEL)newSelector { xxTarget=aTarget; xxSelector=newSelector; return self; } +homWithTarget:aTarget selector:(SEL)newSelector { return [[[self alloc] xxinitWithTarget:aTarget selector:newSelector] autorelease]; } @end
EDIT (Aug 15 2015): Changed SimpleHOM download link to github repo.
7 comments:
Nice work.
Very nice stuff. Thanks a bunch!
I was hoping to do this:
[[_buttons collect] setHidden:YES];
instead of this:
for( UIButton * b in _buttons ) { b.hidden = YES; }
A bit perverse I know but nicer syntax... anyway it crashed. Would be cool to have something like an "each" protocol method on NSArray for that purpose ;-)
Writing code like this:
[[array collect] stringByAppendingString:@"s"]
…results in warning for ‘initialization from distinct Objective-C type’. Is that an error on my part? It can be solved by a simple cast, but obviously I’d rather live without the cast.
If you don't need invocations, you can always use -[NSArray makeObjectsPerformSelector] and -[NSArray makeObjectsPerformSelector:withObject:];
Probably not exactly what you are asking for, but I use it for what you are doing all the time…
michael
Add this to your interface if you want to do nested collects on the same line:
// Get compiler to stop complaining:
@interface NSObject(hom)
-collect;
@end
Then you can do without warnings e.g.:
_buttonActions = [[[[[nodes collect] attributeForName:@"action"] collect] stringValue] retain];
sbwoolside: use -do instead of -collect and you will get better results with messages that return void.
As the name implies, -collect collects the return values.
Post a Comment