diff --git a/NSArray+Ruby.m b/NSArray+Ruby.m index 7cf9620..e8c4f50 100644 --- a/NSArray+Ruby.m +++ b/NSArray+Ruby.m @@ -1,6 +1,7 @@ #import "YOLO.h" #import extern int YOLOArgCount(id); +extern NSMethodSignature *YOLOMS(id); /** The blockToUse variable is necessary or: EXC_BAD_ACCESS @@ -22,7 +23,6 @@ return [NSArray arrayWithObjects:selected count:ii] - @implementation NSArray (RubyEnumerable) - (NSArray *(^)(id))select { @@ -39,16 +39,27 @@ @implementation NSArray (RubyEnumerable) - (NSArray *(^)(id))each { return ^(id frock) { - if (YOLOArgCount(frock) == 3) { - void (^block)(id, NSUInteger) = frock; - NSUInteger ii = 0; - for (id obj in self) - block(obj, ii++); - } else { + NSMethodSignature *sig = YOLOMS(frock); + switch (sig.numberOfArguments) { + case 2: { void (^block)(id) = frock; for (id obj in self) block(obj); + break; } + case 3: { + void (^block)(id, NSUInteger) = frock; + + if ([sig getArgumentTypeAtIndex:2][0] == '@') + block = ^(id o, NSUInteger ii) { + ((void (^)(id, id))frock)(o, @(ii)); + }; + + NSUInteger ii = 0; + for (id obj in self) + block(obj, ii++); + break; + }} return self; }; } diff --git a/README.markdown b/README.markdown index bfe1d3a..2d1b59a 100644 --- a/README.markdown +++ b/README.markdown @@ -58,11 +58,6 @@ id rv = @[@1, @2, @3, @4].map(^(NSNumber *n){ return @(n.intValue * n.intValue); }); // rv => @[@1, @4, @9, @16] - -id rv = @[@1, @2, @3, @4].map(^(NSNumber *n, uint ii){ - return @(n.intValue * ii); -}); -// rv => @[@0, @2, @6, @12] ``` Notably, if you return nil, we skip that element in the returned array. @@ -72,9 +67,27 @@ Justification: 2. We could add NSNull instead, but then we’d be crashy and *YOLOKit is forgiving* 3. In Objective-C calling a method on nil returns nil. When enumerating an array this is equivalent to just skipping that element. Hence: we skip the element. +```objc +id rv = @[@1, @2, @3, @4].map(^(NSNumber *n, uint ii){ + return @(n.intValue * ii); +}); +// rv => @[@0, @2, @6, @12] +``` + +YOLOKit can see how many arguments are in your block. + +```objc +id rv = @[@1, @2, @3, @4].map(^(NSNumber *n, NSNumber *index){ + return @{index: n}; +}); +// rv => @[@{@0: @1}, @{@1: @2}, @{@2: @3}, @{@3: @4}] +``` + +YOLOKit will convert between types for you depending on your block arguments. + ###NSArray.select() ```objc -id rv = @[@1, @2, @3, @4].select(^BOOL(NSNumber *n){ +id rv = @[@1, @2, @3, @4].select(^(NSNumber *n){ return n.intValue % 2 == 0; }); // rv => @[@2, @4] @@ -86,7 +99,7 @@ id rv = @[@1, @2, @3, @4].select(^BOOL(NSNumber *n){ ###NSArray.reject() ```objc -id rv = @[@1, @2, @3, @4].reject(^BOOL(NSNumber *n){ +id rv = @[@1, @2, @3, @4].reject(^(NSNumber *n){ return n.intValue % 2 == 0; }); // rv => @[@1, @3] @@ -251,7 +264,6 @@ id rv = @[@2, @1, @3, @5, @4].sort; id rv = @[@"20", @"1", @3, @5, @"4"].sort; // rv => @[@"1", @3, @"4", @5, @"20"] // handles mixed object types - ``` ###NSArray.pluck() @@ -413,12 +425,12 @@ clear and reads more easily than `isEmpty`. Small wins add up. ###NSArray.all() ```objc -BOOL rv = @[@1, @2, @3].all(^BOOL(id o){ +BOOL rv = @[@1, @2, @3].all(^(id o){ return [o intValue] > 0; }); // rv => YES -BOOL rv = @[@1, @2, @3].all(^BOOL(id o){ +BOOL rv = @[@1, @2, @3].all(^(id o){ return [o intValue] < 3; }); // rv => NO diff --git a/YOLO.m b/YOLO.m index b2d0703..ddf6ccb 100644 --- a/YOLO.m +++ b/YOLO.m @@ -39,7 +39,7 @@ CTBlockDescriptionFlagsHasSignature = (1 << 30) }; -int YOLOArgCount(id block) { +NSMethodSignature *YOLOMS(id block) { struct CTBlockLiteral *blockRef = (__bridge struct CTBlockLiteral *)block; enum CTBlockDescriptionFlags flags = blockRef->flags; @@ -54,7 +54,11 @@ int YOLOArgCount(id block) { } const char *signature = (*(const char **)signatureLocation); - return [NSMethodSignature signatureWithObjCTypes:signature].numberOfArguments; + return [NSMethodSignature signatureWithObjCTypes:signature]; } return 0; } + +int YOLOArgCount(id block) { + return YOLOMS(block).numberOfArguments; +} \ No newline at end of file diff --git a/tests b/tests index 2f886d8..764c73e 100755 --- a/tests +++ b/tests @@ -103,6 +103,10 @@ int main() { @[@1, @2, @3].each(^(id o, int ii){ XCTAssertEqualObjects(o, @(ii+1)); }); + + @[@0, @1, @2].each(^(id o, id ii){ + XCTAssertEqualObjects(o, ii); + }); } - (void)testSelect {