2011年10月10日星期一

Introduction to Blocks in Objective-C – Part 1

Introduction to Blocks in Objective-C – Part 1:


Beginning with iOS 4.0, Apple introduced blocks, which look and operate much like C functions. However, blocks offer many interesting capabilities beyond functions as you know and love them today.


A block is really nothing more than a chunk of code. What makes them unique is that a block can be executed inline as well as passed as an argument into a method/function. Blocks can also be assigned to a variable and called as you would a C function – this, along with a few other topics will be covered in this post.


Block Variable

To define a block variable, the ^ operator is used. Let’s look at a simple block that will return YES/NO based on whether the passed in integer is an even or odd value:



// Defining a block variable
BOOL (^isInputEven)(int) = ^(int input)
{
if (input % 2 == 0)
return YES;
else
return NO;
};


The diagram below describes each section of the block:



To call the block look similar to a C function call:



// Call similar to a C function call
int x = -101;
NSLog(@"%d %@ number", x, isInputEven(x) ? @"is an even" : @"is not an even");


The output for the log statement is:


-101 is not an even number


Blocks and Variable Scope

Notice in the block below that the code in the body references the variable ‘price’ which is defined outside the block.



float price = 1.99;

float (^finalPrice)(int) = ^(int quantity)
{
// Notice local variable price is
// accessible in the block
return quantity * price;
};

int orderQuantity = 10;
NSLog(@"Ordering %d units, final price is: $%2.2f", orderQuantity, finalPrice(orderQuantity));


The output for the log statement is:


Ordering 10 units, final price is: $19.90


Let’s change the price and run the block again:



price = .99;
NSLog(@"Ordering %d units, final price is: $%2.2f", orderQuantity, finalPrice(orderQuantity));


Seems changing the price should update the return value from the block, however, during the block definition the price variable is set to a const, so the output remains the same as before:


The output for the block, with the price variable updated is:


Ordering 10 units, final price is: $19.90


Using __block Storage Modifier

To allow a variable defined outside a block to be mutable, apply the __block storage type modifier:



// Use the __block storage modifier to allow changes to 'price'
__block float price = 1.99;

float (^finalPrice)(int) = ^(int quantity)
{
return quantity * price;
};

int orderQuantity = 10;
price = .99;

NSLog(@"With block storage modifier - Ordering %d units, final price is: $%2.2f", orderQuantity, finalPrice(orderQuantity));


The output for the block, with the price variable updated is:


With block storage modifier – Ordering 10 units, final price is: $9.90


Typedef and Blocks

Before we get to typedef, let’s look at two different ways to define a block variable. In the examples above, the block, parameters and body are defined in one fell swoop:



BOOL (^isInputEven)(int) = ^(int input)
{
if (input % 2 == 0)
return YES;
else
return NO;
};


This is equivalent to creating a block definition:



BOOL (^isInputEven)(int);


and at some point later, defining the body:



isInputEven = ^(int input)
{
if (input % 2 == 0)
return YES;
else
return NO;
};


This above is important, as it may make sense at some point to have the same signature for two distinct blocks. This is where the typdef comes in – a best practice is to use typedef if the same block signature is used for two or more unique operations:


For example, below a typedef is defined for a block that accepts two objects and returns an NSComparisonResult:



typedef NSComparisonResult(^CompareObjects)(id, id);


NSComparisonResult is an enum that can be used to indicate the ordering of items (ascending, descending, same).


With the typedef in place you could now write two blocks, both accepting objects, however each performing a different comparison:



CompareObjects compareAccountBalance = ^(id objectA, id objectB)
{
// Do comparisons here...

// return NSComparisonResult value (NSOrderedAscending, NSOrderedSame, NSOrderedDescending)
};


Here is another block with the same signature, however, there you could change up the logic for comparing the objects:



CompareObjects compareAccountActivity = ^(id objectA, id objectB)
{
// Do comparisons here...

// return NSComparisonResult value (NSOrderedAscending, NSOrderedSame, NSOrderedDescending)
};

没有评论:

发表评论