Thursday, May 17, 2012

Blocks (1)

Lately I've been working on improving my Objective-C coding skills. Although I like PyObjC a lot, it has some issues. I'm thinking I will post some of my exercises on the blog under Quick Objective-C.

To help focus my efforts, I bought a copy of Aaron Hillegass's book
Objective-C Programming.

I like this book a lot. It's a basic introduction that has lots of C as well, but the material has been finely honed by many hours in the classroom. It doesn't cover GUI stuff very much, that's for his other book, the latest edition of which is by Hillegass & Preble.

In order to keep things simple I am not using Xcode. I just invoke the clang compiler from the command line, usually with a #import <Foundation/Foundation.h>, and I use garbage collection.

The example for today is a very basic block, which comes rather late in the book. But before we do that, here is an example of a function pointer in C. We have, in order, a function that takes a single const char * argument, a typedef for a function pointer for functions of that signature, and a function that takes such a function pointer as an argument.

// clang fptr.c -o prog
#include <stdio.h>

void speak(const char *s) { printf("%s", s); }

typedef void (*fptr)(const char *);

void g(fptr f, const char *s){ f(s); }

int main(int argc, const char *argv[]){
    speak("abc\n");
    fptr f = &speak;
    f("def\n");
    g(f, "ghi\n");
    g(speak, "jkl\n");
    return 0;
}

The output is as expected:

> ./prog
abc
def
ghi
jkl

The typedef allows us to declare variables of type fptr. Since the signature for speak matches, we can assign f to the address of speak, and call it just as we would the original function. Either the pointer (or the original function) can be passed into g.

A basic block is declared and defined in a way similar to this. It turns a function into an object that can be passed around, for example, into functions. Of course, blocks are much more powerful than this (e.g. bbum's post).

Here is a basic block. We go to the trouble of making a typedef for this type of block, and define a function f that takes such a block as an argument.

// clang blocks.m -o prog -framework Foundation -fobjc-gc-only
#include <Foundation/Foundation.h>

typedef void (^B) (const char *s);

void f(B b, const char *s){ b(s); }

int main(int argc, char * argv[]) {
    B b = ^(const char *s) {
        printf("%s", s);
    };
    f(b,"abc\n");
    return 0;
}

> ./prog
abc

The similarity to function pointers is pretty clear. The typedef isn't necessary, of course. And if a block is to be used only once, it might even be anonymous, something like a Python lambda.

No typedef:

retval (^block_var) (named_args) = ^(named_args) { ..code.. };

Anonymous versions:
[obj methodWithArg:arg1 block:^(named_args) { ..code.. }];
[obj methodWithArg:arg1 block:(retval (^)(named_args)) { ..code.. }];

It's worth pointing out that the return type is not required to be specified.

An standard example of an anonymous block is to handle the result of a file save dialog (as described in my post here).

[savePanel beginSheetModalForWindow:[NSApp mainWindow]
                  completionHandler:^(NSInteger result) {
                      if (result == NSOKButton) {
                          [savePanel orderOut:self];
                          [self application:NSApp 
                            saveFile:[savePanel filename]];
                      }
                  }];

I'll show an example of capturing state from the enclosing scope in another post.