一、block 介绍
block 是c语言层次的语句,c中的方法比较相似.在一些其他的语言中,block 有时也被称为"closure"(闭包). 她可以被声明为指针变量,作为参数传递以供回调,在异步调用上也非常方便;
block 是一种匿名内联的代码集合,文档上罗列了她的一些功能:
1、有如方法一样的参数列表
2、有返回类型
3、可以在其声明时所在的作用域中占有状态
4、可以在其作用域中选择性的更改状态
5、可以与相同作用域中的其他代码块分享变动的可能性
6、尽管其(栈)作用域被销毁,她可以继续分享和更改在该作用域中定义的状态
block 在创建的时候是被存储于栈中,类似于方法,但是存在于栈中将面临其作用域被销毁的可能 ,所以block可以被复制,也可以被传递至其他线程去执行,复制的block将存在于堆中,如此就算原来对应的栈空间被销户,堆中的block仍然发挥作用,这些对于使用上来说是隐形的,开发者可以不必要手动发送Block_copy()消息进行复制,运行时会自动那么做,同时他还会管理block中的各种性质的变量,这个下面会介绍。
二、用法
1、声明
block的声明类似于方法指针,只是将*换成了^
形式: 返回类型 (^变量名)(参数列表)
下面是文档声明block变量的例子:
void (^blockReturningVoidWithVoidArgument)(void); |
int (^blockReturningIntWithIntAndCharArguments)(int, char); |
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int); |
当然 ,typedef 用起来更简便
typedef float (^MyBlockType)(float, float); |
MyBlockType myFirstBlock = // ... ; |
MyBlockType mySecondBlock = // ... ; |
2、创建
形式:
^(参数列表){
//body
};
若参数为void 则,(void)可以省略,文档例子:
float (^oneFrom)(float); |
oneFrom = ^(float aFloat) { |
float result = aFloat - 1.0; |
return result; |
}; |
3、全局block
使用中,我们经常创建全局block:
#import <stdio.h> |
int GlobalInt = 0; |
int (^getGlobalInt)(void) = ^{ return GlobalInt; }; |
三、block中的变量
代码块中的使用变量,分别是:全局变量(如static修饰的变量),全局方法(严格来说不能教变量),本地变量和参数,__block修饰的变量,静态引入。
文档中罗列了在block 中使用变量的规则:
1、可访问全局变量,包括存在于block作用域中的static 变量
2、可访问block的参数
3、栈变量(非静态,即本体存在于栈中,如int),在程序执行到定义该block的时候将会捕捉该变量的值并当做const变量来使用,相当于复制了一个同名同值的const变量在block中使用。
4、block作用域中声明的本地变量,如果在声明时使用了__block关键字,那么改变量在block中是可写的,否则为可读。若多个Block使用了该__block类型的同一个变量,更改是共享的。
5、block中声明的局部变量,就像方法中声明的局部变量,随意使用
文档中给出了block使用栈变量的两个例子:
1)
int x = 123; |
void (^printXAndY)(int) = ^(int y) { |
printf("%d %d\n", x, y); |
}; |
x=789; //这句是另外加的,可做参考 |
printXAndY(456); // prints: 123 456 |
2)
int x = 123; |
void (^printXAndY)(int) = ^(int y) { |
x = x + y; // error |
printf("%d %d\n", x, y); |
}; |
还有__block情况的例子:
1)
__block int x = 123; // x lives in block storage |
void (^printXAndY)(int) = ^(int y) { |
x = x + y; |
printf("%d %d\n", x, y); |
}; |
printXAndY(456); // prints: 579 456 |
2)
extern NSInteger CounterGlobal; |
static NSInteger CounterStatic; |
{ |
NSInteger localCounter = 42; |
__block char localCharacter; |
void (^aBlock)(void) = ^(void) { |
++CounterGlobal; |
++CounterStatic; |
CounterGlobal = localCounter; // localCounter fixed at block creation |
localCharacter = ‘a‘; // sets localCharacter in enclosing scope |
}; |
++localCounter; // unseen by the block |
localCharacter = ‘b‘; |
aBlock(); // execute the block |
// localCharacter now ‘a‘ |
} |
文档中还介绍了Object和block 变量的关系。block在执行时使用了object变量:
1、如果是通过引用来访问实例变量,那么将会有一个强引用指向self
2、如果是通过值来访问实例变量,那么将会有一个强引用指向该变量
下面的例子展示了两种不同的情况:
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); |
}); |
四、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 |
我们可以将block作为方法的参数进行传递,在很多情况下,需要block参数的地方,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); |
}); |
// Block implementation ends at "}" |
// myCharacters is now { "Charles Condomine", "George", "TomJohn" } |
五、注意点
目前我总结使用代码块的注意点:
1、避免循环引用,如果你在self中定义了block变量,并在self中实现该block的时候,使用了类似self.变量的语句(_变量 也相当于 self.变量),将会造成循环引用。这时请使用__weak 来修饰self;
2、避免悬指针情况,因为block开始的时候是栈存储的,在被copy到堆中前,其可能造成实质作用域和变量作用域不同而导致变量成为悬指针的情况,文档给出了两个例子:
void dontDoThis() { |
void (^blockArray[3])(void); // an array of 3 block references |
for (int i = 0; i < 3; ++i) { |
blockArray[i] = ^{ printf("hello, %d\n", i); }; |
// WRONG: The block literal scope is the "for" loop. |
} |
} |
void dontDoThisEither() { |
void (^block)(void); |
int i = random(): |
if (i > 1000) { |
block = ^{ printf("got i at: %d\n", i); }; |
// WRONG: The block literal scope is the "then" clause. |
} |
// ... |
} |
观看的朋友如发现有误,请指出。谢谢