Usefulness of blocks


#1

I’m trying very hard to wrap my head around blocks, but keep having some issues. My main hangup is that don’t blocks make your code very tightly coupled and therefore less reusable?

For example, in the blocks code in the book, the devowlizer block makes explicit reference to the newStrings array that was declared outside of the block.

[code]NSMutableArray *newStrings = [NSMutableArray array];

    // Declare the block variable
    void (^devowelizer)(id, NSUInteger, BOOL *);
    
    devowelizer = ^(id string, NSUInteger i, BOOL *stop) {
        NSMutableString *newString = [NSMutableString stringWithString:string];
        
        // iterate over the array of vowels
        for (NSString *s in vowels) {
            NSRange fullRange = NSMakeRange(0, newString.length);
            [newString replaceOccurrencesOfString:s withString:@"" options:NSCaseInsensitiveSearch range:fullRange];
        }
        
        [newStrings addObject:newString];[/code]

Doesn’t that make this block difficult to reuse without manually going in and changing that array its referencing? And doesn’t that go against many of the principles of object-oriented design? I’m fairly certain I’m missing something, as obviously blocks have become quite important in iOS development. Just really want to understand what it is that I’m missing.


#2

I’m having a hard time here as well. Scope seems weird as you’re suggesting. I think since the block is defined in the same overall block where newStrings is that it’s okay (obviously has to be eh, as it works). I think that’s where it’s scoping though. This would still hard to make it reusable except in main in this example. I need to read up on this more. So far the two concepts I don’t comprehend are this one and callbacks, which I know I’ll see a lot more examples of in the Stanford iTunes class which I just started watching. Initial exposure here (the point I think of the chapter), and some practice with other examples should help. I definitely reread the callbacks and block chapters though. Just wanted you to know you’re not alone, brother!


#3

Reading over the docs on blocks at developer.aple.com is pretty interesting.

It’s a feature that was introduced hand in hand with Grand Central Dispatch and that seems to be where a huge benefit comes from.

Today’s computers have multiple CPUs with multiple cores, to take advantage of that you need threaded apps.

It used to be that to make a multithreaded app you needed to break your app up in to threads and coordinate everything yourself. GCD changes a lot of that.

With GCD you can more or less just fill queues with work and the system figures it out. Does anyone want to guess how you encapsulate code into a unit of work to place in a queue? :smiley: Of course all of this is a grand oversimplification, but it makes the point.

There are other advantages to blocks as well, but the ability to work with GCD seemed to jump out at me the most as one I could conceptually grasp.


#4

one example without going into too much detail.

I had to come up with a way to receive iOS rotation events without using objects and pointers… So I passed the notification service a block of code for it to run when the device rotated.

so
addObserverForName:UIDeviceOrientationDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
};

instead of
[nc addObserver:<#(id)#> selector:<#(SEL)#> name:<#(NSString *)#> object:<#(id)#>]

nice…


#5

I doubt Chris is still looking for answers at this point, but it occurs to me that there’s a different principle at play here: coupling vs. reuse.

Blocks aren’t really intended to be reused. Many times, a block is used for a sophisticated iterator, for example, and you would no more reuse the block than you would reuse the contents of a for loop.

Instead, they provide a very tight local data coupling mechanism while allowing for very loose intra-object coupling. The client doesn’t have to know much about the server to pass a block, and the server doesn’t have to know anything about the client to execute it.

One common alternative approach would be to pass a function pointer or method name to the server, but then you have to write extra code to expose that local array via a method, just so the server has something to call, resulting in far more maintenance headaches.


#6

My take is that blocks are essentially a syntactical convenience. For me they can make code easier to read and understand, especially asynchronous code. They essentially provide a way to executing a simple snippet of code without all of the baggage of creating a new method or function, passing arguments, etc. For example, assume I want to execute some code on the next iteration of the current run loop. Normally I would do something like:

[obj performSelector:@selector(foo) withObject:someObj afterDelay:0.0]

The above requires that I implement ‘foo’ on ‘obj’ somewhere, most likely not near the call site, and that I pass all the arguments I need through ‘someObj.’ What if not all my arguments are ‘ids’. Moreover, as I’m reading this code, I have to go find the implementation of ‘foo’ to get the appropriate conceptual context so that I can figure out what the current code I’m looking at does. It’s much simpler to do something like:

SomeObj* obj = <>;
int a = 10;

dispatch_async(dispatch_get_current_queue(), ^{
     if (a > 5) {
         [obj someMethod:a]
     }
});

Because ‘a’ and ‘obj’ are within the block’s enclosing scope they automatically become available to the block code (as constants)–look ma, passing both a scaler and id into the block with ease. I also don’t have to look somewhere else in the code to determine what gets executed asynchronously.