Stanford Cs193P: Developing Applications For Iphone 4, Ipod Touch, & Ipad Fall 2010
Stanford Cs193P: Developing Applications For Iphone 4, Ipod Touch, & Ipad Fall 2010
Developing Applications for iPhone 4, iPod Touch, & iPad Fall 2010
Today
Blocks
Language syntax for declaring a function on the y.
C API for leveraging blocks to make writing multithreaded code much easier.
Blocks
What is a block?
A block of code (i.e. a sequence of statements inside {}). Usually included in-line with the calling of method that is going to use the block of code. Very smart about local variables, referenced objects, etc.
This NSLog()s every key and value in aDictionary (but stops if the key is ENOUGH).
Blocks
Can use local variables declared before the block inside the block
double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSLog(@value for key %@ is %@, key, value); if ([@ENOUGH isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; } }];
BOOL stoppedEarly = NO; double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSLog(@value for key %@ is %@, key, value); if ([@ENOUGH isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; stoppedEarly = YES; // ILLEGAL } }];
Blocks
Unless you mark the local variable as __block
__block BOOL stoppedEarly = NO; double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSLog(@value for key %@ is %@, key, value); if ([@ENOUGH isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; stoppedEarly = YES; // this is legal now } }]; if (stoppedEarly) NSLog(@I stopped logging dictionary values early!);
Because instance variables are really just a special case of an object being accessed in the block. Lets talk some more about that ...
Stanford CS193p Fall 2010
Blocks
So what about objects accessed inside the block?
NSString *stopKey = [@Enough uppercaseString]; __block BOOL stoppedEarly = NO; double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSLog(@value for key %@ is %@, key, value); if ([stopKey isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; stoppedEarly = YES; // this is legal now } }]; if (stoppedEarly) NSLog(@I stopped logging dictionary values early!);
is automatically retained until the block goes out of scope or the block itself is released. Why does that matter? And what does it mean for the block itself to be released?
stopKey
Stanford CS193p Fall 2010
Blocks
Imagine we added the following method to CalculatorBrain
- (void)addUnaryOperation:(NSString *)operation whichExecutesBlock:...;
This method adds another operation to the brain like sqrt which you get to specify the code for. For now, well not worry about the syntax for passing the block. (but the mechanism for that is the same as for dening enumerateKeysAndObjectsUsingBlock:).
Imagine if secret was not automatically retained here. What would happen later when this block executed (when MoLtUaE operation was pressed)? Bad things. Luckily, secret is automatically retained.
Blocks
How would we dene that method?
typedef double (^unary_operation_t)(double op);
Blocks are kind of like objects with an unusual syntax for declaring variables that hold them. Usually if we are going to store a block in a variable, we typedef a type for that variable, e.g., This declares a type called unary_operation_t for variables which can store a block. (specically, a block which takes a double as its only argument and returns a double) Then we could declare a variable, square, of this type and give it a value ...
unary_operation_t square; square = ^(double operand) { return operand * operand; }
And then use the variable square like this ... double squareOfFive = square(5.0); / squareOfFive would have the value 25.0 after this / (You dont have to typedef, for example, the following is also a legal way to create square ...)
double (^square)(double op) = ^(double op) { return op * op; };
Stanford CS193p Fall 2010
Blocks
We could then use the unary_operation_t to dene our method
typedef double (^unary_operation_t)(double op); - (void)addUnaryOperation:(NSString *)op whichExecutesBlock:(unary_operation_t)opBlock { [operationDictionary setObject:opBlock forKey:op]; }
Notice that we can treat the block somewhat like an object (adding it to a dictionary, in fact). The only messages we might send to a block, though, are copy, retain, release or autorelease. Unfortunately, blocks are allocated initially on the stack (theyre not really objects in that way). To get a heap-allocated block, wed send [opBlock copy] as our argument to setObject:forKey:. Wed also want to autorelease that copy (since it gets retained by the dictionary). Later in our CalculatorBrain we could use an operation added with the method above like this ...
- (double)performOperation:(NSString *)operation { unary_operation_t unaryOp = [operationDictionary objectForKey:operation]; if (unaryOp) { self.operand = unaryOp(self.operand); } . . . }
Blocks
Back to our calling of this method
NSNumber *secret = [NSNumber numberWithDouble:42.0]; [brain addUnaryOperation:@MoLtUaE whichExecutesBlock:^(double operand) { return operand * [secret doubleValue]; }];
We said earlier that the object secret will be retained until the block is released. So when will this block be released? The block will be released if and when CalculatorBrain removes it from its operationDictionary. Or when the CalculatorBrain is released (it will release operationDictionary in its dealloc). As you might expect, if you access an instance variable in your block, self will be retained.
Blocks
Back to blocks as method arguments
When a block is an argument to a method and is used immediately, often there is no typedef. Here is the declaration of the dictionary enumerating method we showed earlier ...
- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block;
Notice, no typedef for this block. The syntax is exactly the same as the typedef except that the name of the typedef is not there. For reference, heres what a typedef for this argument would look like this ...
typedef void (^enumeratingBlock)(id key, id obj, BOOL *stop);
Blocks
Some shorthand allowed when dening a block
(Dening means you are writing the code between the {}.) You do not have to declare the return type if it can be inferred from your code in the block. If there are no arguments to the block, you do not need to have any parentheses. Recall this code (no return type, see?):
NSNumber *secret = [NSNumber numberWithDouble:42.0]; [brain addUnaryOperation:@MoLtUaE whichExecutesBlock:^(double operand) { return operand * [secret doubleValue]; }];
Blocks
When do we use blocks in iOS?
Enumeration View Animations (more on that later in the course) Sorting (sort this thing using a block as the comparison method) Notication (when something happens, execute this block) Error handlers (if an error happens while doing this, execute this block) Completion handlers (when you are done doing this, execute this block)
Get blocking activity (e.g. network) out of our user-interface (main) thread. Do time-consuming activity concurrently in another thread.
}); }
}); }); }
Problem! NSManagedObjectContext is not thread safe, so we cant call photo.URL in downloadQueues thread!
Coming Up
Demo
Add a PhotoViewController to Shutterbug Stop it from blocking the main thread
Homework
Current homework still due on Wednesday Next homework might be assigned next Tuesday, due the following Monday
Next Lecture