Block 这个东西比较难懂,如果没学过其他有类似功能的语言还真搞不明白。
OK,先来介绍一下它是什么东西吧!
Block 是iOS在4.0之后新增的程式语法,严格来说block的概念并不算是基础程式设计的范围,对初学者来说也不是很容易了解,但是在iOS SDK 4.0之后,block几乎出现在所有新版的API之中,换句话说,如果不了解block这个概念就无法使用SDK 4.0版本以后的新功能,我靠!现在都什么版本了!还4.0呢!没关系,这更说明了我们必须得学。(其实我觉得这一段是废话...没有用)
一、Block 简介
Block 实体是这样的: ^(传入参数列){行为主体}; //这里面的名词...可以自己揣摩揣摩,就是参数和方法的意思吧,个人觉得...
举个栗子!^(int a){return a*a};
如何使用呢: int aa = ^(int a){return a*a}(5); //wtf?!这玩意根本不能看好吗?!
我们来用另外一个东西简化它!叫做Block Pointer。
先来看看Block Pointer 实体是什么样子的: 回传值(^名字)(参数列);
OK,还是上面那个栗子:
int (^square)(int);
square = ^(int a){return a*a};
int aa = square(5); //看着爽多了!
虽然说是简介,但是还是要有进阶喔!下面进阶看看。
这里就用上面的square来继续操作。、
首先不要想太多,暂时就把Block这个东西看做是一种数据类型,例如,int、NSString、bool
我们定义一个方法,用来传入这个参数
如果是普通方法,那就是这样定义
-(void)testMethod:(int)number;
那如果是将block作为参数传递的方法呢,看看下面
-(void)testBlockMethod:(int(^)(int)) square;
当然,这只是定义了一个方法,需要传这样类型的Block
你必须自己定义一个Block然后传它:
int (^mySquare)(int) = ^(int a){return a*a};
[self testBlockMethod:mySquare];
这样是不是明白多了?
有没有发现,Block其实跟方法是挺像的东西,又跟一个变量或者常量类似,那么我们就可以这么做:
int outNumber = 4;
int (^square)(int) = ^(int a){
return outNumber * a;
}
int result = square(2); //result 的值最后就是8
下面有一段代码需要我们去理解一下:
int outNumber = 4;
int (^square)(int) = ^(int a){
return outNumber * a;
}
outNumber = 8;
int result = square(2); //result 的值最后还是8,而不是16
为什么会这样呢?其实咱们定义的square在使用outNumber的时候是对outNumber使用了copy操作的
因此你改变原先的outNumber是不会对Block内使用的数字进行操作的
那,我们如何做呢?要么,在使用Block之前就改变,要么,就在Block里面改变
int outNumber = 4;
int (^square)(int) = ^(int a){
outNumber = 6;
return outNumber * a;
}
outNumber = 8;
int result = square(2); //result 的值最后还是12,因为outNumber被改变了,但是只是Block内部的被改变了
那,如果我们想要Block内部改变后外部继续使用呢?
这,就要使用到__block声明了,注意 __block 前面的下划线是两个哦,明眼人都看的见!
__block int outNumber = 4;
int (^square)(int) = ^(int a){
outNumber = 8;
return outNumber * a;
}
int result = square(2);
NSlog(@"outNumber = %@", outNumber);//这个时候如果outNumber没有声明__block,就会输出:outNumer = 4,但是我们加了这个声明,就会输出outNumber = 8
------------------------------------------------------ 分割线 --------------------------------------------------------------------------------------------
以上呢就算是比较基础的部分了,主要将的是怎么定义等问题,下面就要讲讲怎么使用的问题了
首先说说block最有可能出现的问题,那就是内存泄露,这个就算你用ARC,也可能会出现问题
我不会说明怎么去避开这些问题,我只能告诉你,这个问题为什么会发生,就是说,原理
首先看下面的代码(伪代码):
int (^MyBlock)(int);
MyBlock genBlock();
int main(){
MyBlock outBlock = genBlock();
int result = outBlick(5);
NSLog(@"result is %d",[outBlock retainCount] );//
NSLog(@"result is %d",result );
return 0;
}
MyBlock genBlock(){
int a = 3;
MyBlock inBlock = (^int n){
return n*a;
};
return inBlock;
}
这段代码,会报错:
Segmentation fault (点击查看关于Segmentation fault的资料)
为什么呢?是因为inBlock在他自己return的时候retainCount就已经减一然后为0了,这个时候它就会被释放
后面你再使用的时候,可不就会出现问题了?
想解决的话也很容易,将inBlock这个代码块copy一遍就好了。
在最后的时候
MyBlock genBlock() {
int a = 3;
MyBlock inBlock = ^(int n) {
return n*a;
};
return [inBlock copy] ;
}
对了,如果你没有使用ARC,记得自己去释放它就可以了 。
还记得分割线之前的 __block 吗?我们再将一个关于它的内存管理问题
看下面的代码
MyBlock genBlock() {
int a = 3;
NSMutableString * myString = [NSMutableString string];
MyBlock inBlock = ^(int n) {
NSLog(@"retain count of string %d",[myString retainCount]);
return n*a;
};
return [inBlock copy] ;
}
这样输出各位看官会认为是什么样呢?答案是!!!!
retain count of string 2
这个原因很简单,问题就在于Block自己,还记得前面说过的事情么,这个myString在Block里面使用是会被copy的
所以,retainCount 为 2 是正常的,那如果我们按下面这样做...
MyBlock genBlock() {
int a = 3;
__block NSMutableString * myString = [NSMutableString string];
MyBlock inBlock = ^(int n) {
NSLog(@"retain count of string %d",[myString retainCount]);
return n*a;
};
return [inBlock copy] ;
}
就会输出:
retain count of string 1
恩....自己领悟去吧!
以上就是Block 的基础性知识了,我写成这样也是为了让那些理解事物跟我一样奇葩的童鞋能看懂Block
当然,其实你只要用心去学去研究了,什么东西都阻挡不了你推进的脚步