Getting Started with Blocks
Declaring and Using a Block
使用^操作符来声明一个block变量并且指明了block的开始。body在{}中如下的例子:
int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};
};
例子解释如下:
你可以看到block body中可以访问 {}和自己是同一个scope的变量。
如果你将block声明为一个变量,那么你可以像函数一样使用它,例如:
int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};
printf("%d", myBlock(3));
//prints "21"
Using a Block Directly
许多情况下你不需要声明block变量,而是简单写block inline作为参数。如下的例子使用qsort_b函数。qsort_b和标准的qsort_r函数很类似,但是将block作为了参数。
char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) { char *left = *(char **)l; char *right = *(char **)r; return strncmp(left, right, 1);
});
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
Blocks with Cocoa
如下的例子展示的就是如何使用block和nsarray方法sortedArrayUsingComparator:这个方法有一个block的参数。为了说明,这里将block定义成了NSComparator local 变量。
NSArray *stringsArray = @[ @"string 1", @"String 21", @"string 12", @"String 11",
@"String 02" ];
static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
NSWidthInsensitiveSearch | NSForcedOrderingSearch; NSLocale *currentLocale = [NSLocale currentLocale];
NSComparator finderSortBlock = ^(id string1, id string2) {
NSRange string1Range = NSMakeRange(0, [string1 length]);
return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
};
NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock]; NSLog(@"finderSortArray: %@", finderSortArray);
/* Output: finderSortArray: (
"string 1", "String 02", "String 11", "string 12", "String 21"
) */
__block Variables
__block类型就说明了这个变量在block body中可以被改变。
NSArray *stringsArray = @[ @"string 1",
@"String 21", // <- @"string 12", @"String 11", @"Strîng 21", // <- @"Striñg 21", // <- @"String 02" ];
NSLocale *currentLocale = [NSLocale currentLocale]; __block NSUInteger orderedSameCount = 0;
NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {
NSRange string1Range = NSMakeRange(0, [string1 length]);
NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];
if (comparisonResult == NSOrderedSame) { orderedSameCount++;
}
return comparisonResult; }];
NSLog(@"diacriticInsensitiveSortArray: %@", diacriticInsensitiveSortArray); NSLog(@"orderedSameCount: %d", orderedSameCount);
/* Output:
diacriticInsensitiveSortArray: ( "String 02",
"string 1", "String 11", "string 12", "String 21",
"StrU00eeng 21",
"StriU00f1g 21" )
orderedSameCount: 2 */
Conceptual Overview
Block Functionality
- block 是一个匿名的code的inline collection
- 有一个类似function的类型参数
- 有一个返回类型
- 可以捕获它定义范围内的状态
- 可以有选择的改变定义范围内的状态
- 可以在定义范围销毁后继续共享和改变定义状态
Usage
blocks代表了小规模的,自容的代码片段。它们在执行并发的任务时作为封装单元尤其重要。block有别于传统callback函数的优点是:
1,它们允许你在方法执行后的某一个调用的地方书写代码
2,它们允许访问本地变量
Declaring and Creating Blocks
Declaring a Block Reference
void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
没有返回类型的block必须显示表明为void类型
你也可以创建block类型当你在多个地方使用特定的signature
typedef float (^MyBlockType)(float, float);
MyBlockType myFirstBlock = // ... ; MyBlockType mySecondBlock = // ... ;
Creating a Block
以下的例子定义了一个简单的block然后分配给了一个之前声明的变量oneFrom。
float (^oneFrom)(float);
oneFrom = ^(float aFloat) { float result = aFloat - 1.0; return result;
};
Global Blocks
#import <stdio.h>
int GlobalInt = 0; int (^getGlobalInt)(void) = ^{ return GlobalInt; };
Blocks and Variables
5中类型的变量
- global variables, including static locals
- global functions
- local variables and parameters form an enclosing scope
- __block varibales
- const improts
Object and Block Variables
Objective-C Objects
当copy一个block时,它会创建一个object变量的强引用。如果你在一个执行方法中说使用block:
如果你通过引用来访问一个实例变量,就会产生self的强引用
如果你通过值来访问一个实例变量,就会产生这个变量的强引用
dispatch_async(queue, ^{ // instanceVariable is used by reference, a strong reference is made to self doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable; dispatch_async(queue, ^{
/* localVariable is used by value, a strong reference is made to localVariable (and not to self).
*/
doSomethingWithObject(localVariable); });
Using Blocks
Invoking a Block
如果你将block声明为变量,你可以在函数中使用它,例如:
int (^oneFrom)(int) = ^(int anInt) { return anInt - 1;
};
printf("1 from 10 is %d", oneFrom(10)); // Prints "1 from 10 is 9"
float (^distanceTraveled)(float, float, float) = ^(float startingSpeed, float acceleration, float time) {
float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
return distance; };
float howFar = distanceTraveled(0.0, 9.8, 1.0); // howFar = 4.9
Using a Block as a Function Argument
你可以将block作为一个function参数进行传递。在大多数时候,你不需要声明blocks,只需要简单以内连形式来执行就可以了。以下的例子:
char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) { char *left = *(char **)l; char *right = *(char **)r; return strncmp(left, right, 1);
}); // Block implementation ends at "}"
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
下面的例子展示了如何在dispatch_apply函数中使用block。
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
函数提交一个block到dispatch queue中。有三个参数。
#include <dispatch/dispatch.h>
size_t count = 10;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(count, queue, ^(size_t i) { printf("%u ", i);
});
###################################################
void func(void (^)(void));
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^hw)(void) = ^{NSLog(@"hellow world!");};
func(hw);
}
return 0;
}
void func(void (^b) (void))
{
NSLog(@"going to invoke the passed in block now.");
b();
}
最后打印的结果为:
2015-01-07 14:41:38.296 FunWithBlocks[539:69766] going to invoke the passed in block now.
2015-01-07 14:41:38.298 FunWithBlocks[539:69766] hellow world!
Using a Block as a Method Argument
cocoa提供了一系列使用blocks的方法。你传递block作为方法参数。例子:
NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"]; NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
BOOL (^test)(id obj, NSUInteger idx, BOOL *stop); test = ^(id obj, NSUInteger idx, BOOL *stop) {
if (idx < 5) { if ([filterSet containsObject: obj]) {
return YES; }
}
return NO; };
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test]; NSLog(@"indexes: %@", indexes);
/* Output:
indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]
*/
Copying Blocks
其实你不需要copy一个block。只有当你想在scope destruction之后仍然想使用block的时候才会用到。copying将block移动到heap中。
Block_copy(); Block_release();
Debugging
你可以在blocks中设置断点和单步跟踪。
You can set breakpoints and single step into blocks. You can invoke a block from within a GDB session using invoke-block, as illustrated in this example:
If you want to pass in a C string, you must quote it. For example, to pass this string into the doSomethingWithString block, you would write the following:
$ invoke-block doSomethingWithString ""this string""