"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:
@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
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.That leaves the actual trampoline, which is really just an implementation detail for conveniently creating NSInvocation objects.
@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
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/.
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