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

当然,其实你只要用心去学去研究了,什么东西都阻挡不了你推进的脚步