1、block 介绍闭包
block 是c语言层次的语句,c中的方法比较类似.在一些其余的语言中,block 有时也被称为"closure"(闭包). 她能够被声明为指针变量,做为参数传递以供回调,在异步调用上也很是方便;app
block 是一种匿名内联的代码集合,文档上罗列了她的一些功能:dom
一、有如方法同样的参数列表异步
二、有返回类型async
三、能够在其声明时所在的做用域中占有状态oop
四、能够在其做用域中选择性的更改状态ui
五、能够与相同做用域中的其余代码块分享变更的可能性spa
六、尽管其(栈)做用域被销毁,她能够继续分享和更改在该做用域中定义的状态线程
block 在建立的时候是被存储于栈中,相似于方法,可是存在于栈中将面临其做用域被销毁的可能 ,因此block能够被复制,也能够被传递至其余线程去执行,复制的block将存在于堆中,如此就算原来对应的栈空间被销户,堆中的block仍然发挥做用,这些对于使用上来讲是隐形的,开发者能够没必要要手动发送Block_copy()消息进行复制,运行时会自动那么作,同时他还会管理block中的各类性质的变量,这个下面会介绍。指针
2、用法
一、声明
block的声明相似于方法指针,只是将*换成了^
形式: 返回类型 (^变量名)(参数列表)
下面是文档声明block变量的例子:
void (^blockReturningVoidWithVoidArgument)(void); |
int (^blockReturningIntWithIntAndCharArguments)(int, char); |
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int); |
固然 ,typedef 用起来更简便
typedef float (^MyBlockType)(float, float); |
MyBlockType myFirstBlock = // ... ; |
MyBlockType mySecondBlock = // ... ; |
形式:
^(参数列表){
//body
};
若参数为void 则,(void)能够省略,文档例子:
float (^oneFrom)(float); |
oneFrom = ^(float aFloat) { |
float result = aFloat - 1.0; |
return result; |
}; |
三、全局block
使用中,咱们常常建立全局block:
#import <stdio.h> |
int GlobalInt = 0; |
int (^getGlobalInt)(void) = ^{ return GlobalInt; }; |
3、block中的变量
代码块中的使用变量,分别是:全局变量(如static修饰的变量),全局方法(严格来讲不能教变量),本地变量和参数,__block修饰的变量,静态引入。
文档中罗列了在block 中使用变量的规则:
一、可访问全局变量,包括存在于block做用域中的static 变量
二、可访问block的参数
三、栈变量(非静态,即本体存在于栈中,如int),在程序执行到定义该block的时候将会捕捉该变量的值并当作const变量来使用,至关于复制了一个同名同值的const变量在block中使用。
四、block做用域中声明的本地变量,若是在声明时使用了__block关键字,那么改变量在block中是可写的,不然为可读。若多个Block使用了该__block类型的同一个变量,更改是共享的。
五、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 |
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变量:
一、若是是经过引用来访问实例变量,那么将会有一个强引用指向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); |
}); |
4、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" } |
5、注意点
目前我总结使用代码块的注意点:
一、避免循环引用,若是你在self中定义了block变量,并在self中实现该block的时候,使用了相似self.变量的语句(_变量 也至关于 self.变量),将会形成循环引用。这时请使用__weak 来修饰self;
二、避免悬指针状况,由于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. |
} |
// ... |
} |
观看的朋友如发现有误,请指出。谢谢