Search
Rich's Mad Rants
Powered by Squarespace

Entries in Objective-C (7)

Sunday
Mar112012

Fun with Blocks

In last week's class when we were talking about blocks, one of the students asked if any methods ever took block arguments with return values. I couldn't think of any API calls off hand--but I also couldn't think of any reasons why it wouldn't be possible. In fact, I realized it could be used to easily map from one array to another.

So I created a quick proof-of-concept, using a category on NSArray with a single method:

 

- (NSArray*)mapUsingBlock:(id (^) (id object)) block {

    NSMutableArray* newArray = [NSMutableArray arrayWithCapacity:[self count]];

     for (id object in self) {
         [newArray addObject:block(object)];
     }

    return [newArray copy];
}

 

This simply iterates over the array and passes each item from our array to our block. It then stores all the return values in a new array, which it returns.

I could then call the code as shown below:

 

NSArray* names = [NSArray arrayWithObjects: @"Bob", @"Sally", @"Mary", @"Jim", nil];

 

NSArray* allCaps = [names mapUsingBlock:(id)^(id object) {

 

    return [object uppercaseString];

}];

 

NSLog(@"Names = %@", names);

NSLog(@"All Caps = %@", allCaps);

 

NSArray* pigLatin = [names mapUsingBlock:^id(id object) {

 

    NSString* firstCharacter = [[object substringToIndex:1] lowercaseString];

    NSString* restOfName = [[object substringFromIndex:1] capitalizedString];

 

    return [NSString stringWithFormat:@"%@%@ay", restOfName, firstCharacter];

}];

 

NSLog(@"Pig Latin: %@", pigLatin);

 

 

NSArray* counts = [names mapUsingBlock:^id(id object) {

 

    return [NSNumber numberWithInt:[object length]];

}];

 

NSLog(@"Counts: %@", counts);


id (^booBlock) (id object) = ^id(id object) {return @"Boo";};

NSArray* boo = [names mapUsingBlock:booBlock];

 

NSLog(@"Boo: %@", boo);


The first example block simply returns an all-caps version of its argument. The second somewhat naively converts its argument into pig latin. The third returns an NSNumber holding the length of its argument--showing that the new array does not need to contain the same type of object as the original array. Finally, the last block simply returns a string value--it doesn't use the block's argument at all.

These examples also shows a few different ways of declaring our block. The first three define the block in-line. The last example stores the block in a local variable before passing it into our method.

Calling this code generates the following output:

 

2012-03-11 00:11:17.841 ArrayMap[13029:403] Names = (

    Bob,

    Sally,

    Mary,

    Jim

)

2012-03-11 00:11:17.844 ArrayMap[13029:403] All Caps = (

    BOB,

    SALLY,

    MARY,

    JIM

)

2012-03-11 00:11:17.846 ArrayMap[13029:403] Pig Latin: (

    Obbay,

    Allysay,

    Arymay,

    Imjay

)

2012-03-11 00:11:17.848 ArrayMap[13029:403] Counts: (

    3,

    5,

    4,

    3

)

2012-03-11 00:11:17.848 ArrayMap[13029:403] Boo: (

    Boo,

    Boo,

    Boo,

    Boo

)


So far, so good. But, let's look at Objective-C's block syntax for a second. First off, when calling mapUsingBlock: there are (at least) two valid syntaxes for creating our block: (id)^(id object) or ^id(id object). Neither of these match the syntax used when defining the method: (id (^) (id object)) block. Additionally, all three of these are different from the syntax used to declare local block variables: id (^blockName) (id object).

Dont' get me wrong. I love blocks. Of course, I was first exposed to them (or lambdas, closures, or whatever name you may choose to call these constructs) in languages like Lisp, Smalltalk and Ruby. I think they're a great addition to Objective-C, and I generally prefer Objective-C APIs that use blocks. I mean, seriously. Could we get a block-based API for UIAlertView? That would be awesome.

I even find myself writing my own methods to consume blocks. And not just toy examples like this. I'm all in on this whole block thing--and it looks like Apple is too. This became especially clear with iOS 5. There are a number of features (iCloud and Twitter come to mind) that you just cannot use without blocks.

Having said all that, let's face the facts. The block syntax is ugly and inconsistent, and it often drives me crazy. I find it almost impossible to remember the correct syntax from one use to the next. The only saving grace is Xcode's autocompletion. It generally does a great job creating the proper syntax for block arguments when calling block-based methods. Which helps a lot when using block-based APIs. Still, if you start creating your own block-based methods, you're on your own.

Monday
Jul252011

Hey, my book is out. Sort of…

OK, so my book is out. At least, part of my book is available in Apple's iBook store. Check it out here.

Ebook Cover

Just some background, this is not the complete book. Rather, we had hoped to extract the chapter on Objective-C and release it as a stand-alone ebook. The idea was to have it out before WWDC, so I could promote it at the conference. That didn't happen, but that's OK. I'm excited to have it out now.

This is a thorough introduction to Objective-C, specifically taylored for iOS developers. It's not all inclusive, but at 58 pages, it does have a lot of bang for the buck (or three-bucks, ninty-nine cents--as the case may be).

If anyone's interested in the actual print edition, I'm still working on it. I'm in the middle of rewriting everything, updating it all to iOS 5.0. Which also means it cannot be released (and I cannot say much about the contents) until Apple releases us from our NDA. Still, the chapter that is in the print book has been rewritten, and there are a few significant differences between the two.

Think of it this way. If you need to support older devices (iOS 4 and earlier), then this e-book is for you. The print book will focus on iOS 5.0 and beyond. Let's be honest, you should probably buy both.

If you pick up a copy, let me know what you think.

Thanks,

-Rich-

Page 1 2