1 block的基本概念
1.1 block的产生和用途
代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量、作为参数、作为返回值,特殊地,Block还可以保存一段代码,在需要的时候调用,目前Block已经广泛应用于iOS开发中。Block实际上是OC语言对闭包的实现,是带有自动变量值的匿名函数。Block既可以定义在函数内部也可以定义在函数外部。只有在调用block的时候,block{}内的代码才会执行。
1.2 闭包的概念
闭包就是一个函数,或者一个指向函数的指针,加上这个函数执行的非局部变量。闭包允许一个函数访问声明该函数运行上下文中的变量,甚至可以访问不同运行上文中的变量。
1.3 block的声明与定义
int (^myBlock)(int) = ^(int num){
return num*num;
};
int 代表返回类型 ^代表的这是一个block
myBlock代表block的名字 (int)代表参数类型
(int num)代表形参的类型及名字
在定义block的时候一定不要忘记在最后加上分号
block是一个对象,内部是一个结构体,在创建block 的时候就把它的指针传给了block,所以可以直接调用block块
但是为了使用block方便,一般在定义block的时候会用typedef简化block如:typedef void(^myBlock)(int) ;
1.4 block的实质
通俗点说,block是一个结构体,它里面包含了函数指针以及block外部上下文变量等信息,这个函数指针指向的是在定义block时的代码块。
1.5 block最常使用的情况
block最常使用的方式:界面传值;回调
2 Block对变量的访问与修改
2.1 block对局部变量的访问与修改
(1) block1 = ^{
NSLog(@"哈哈哈哈哈哈");
};
block1();
(2) NSInteger aaa = 100;
block2 = ^{
aaa++; //会报错
NSLog(@"aaa的值为%ld",aaa);
};
(3)__block NSInteger aaa = 100;
block2 = ^{
aaa++;
NSLog(@"aaa的值为%ld",aaa); //aaa = 101
};
(4) NSInteger aaa = 100;
block5 = ^{
NSLog(@"aaa的值为%ld",aaa);//aaa = 100
};
aaa = 105;
block5();
(5)__block NSInteger ddd = 10;
block7 = ^{
ddd = ddd +1;
NSLog(@"ddd现在的值为%ld",ddd);
};
ddd = 100;
block7();
通过(1)说明block只有在调用的时候其内部代码才会执行,不调用其内部代码不会执行,通过(2)和(3)说明block可以访问局部变量,但是不能对其进行修改,如果要在block里面修改局部变量的值,那么要在局部变量前面加_ block修饰符 。通过(4)和(5)可以看出如果局部变量不加 block来进行修饰,那么当在block定义后block调用前修改局部变量的值,那么block里面的局部变量依然是block定义时的值(因为在block定义的时候就将局部变量的值传给了block所指向的结构体,所以局部变量的改变不会影响block里面的局部变量,但是block里面的局部变量也是不可修改的),如果局部变量前面加 _block修饰,那么当在block定以后block执行前对局部变量进行修改,block里面的局部变量值也会进行相应的改变(因为用__block声明局部变量后,在block里面是把局部变量的指针传给block指向的结构体的,所以其值可以被改变)。
2.2block对全局变量的访问
(1)// 声明全局变量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};// 调用后控制台输出"global = 100"
myBlock();
(2)// 声明全局变量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);// 调用后控制台输出"global = 101"
};
global = 101;
myBlock();
(3)// 声明全局变量global
int global = 100;
void(^myBlock)() = ^{
global ++;
NSLog(@"global = %d", global);// 调用后控制台输出"global = 101"
};
myBlock();
结论:block可以直接对全局变量进行访问和修改(全局变量所占用的内存只有一份,供所有函数共同调用,在Block定义时并未将全局变量的值或者指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的)
2.3 block对全局静态变量的访问
(1)// 声明静态变量global
static int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);// 调用后控制台输出"global = 100"
};
myBlock();
(2)// 声明静态变量global
static int global = 100;
void(^myBlock)() = ^{
NSLog(@"global= %d", global);// 调用后控制台输出"global = 101"
};
global = 101;
myBlock();
(3)// 声明静态变量global
static int global = 100;
void(^myBlock)() = ^{
global ++;
NSLog(@"global = %d", global);// 调用后控制台输出"global = 101"
};
myBlock();
结论:block里面可以直接访问和修改静态全局变量(因为在Block定义时便是将静态变量的指针传给Block变量所指向的结构体,因此在调用Block之前对静态变量进行修改会影响Block内部的值,同时内部的值也是可以修改的)
3 block的使用方式
block的使用与代理很类似,代理是让某个类满足自己的某个协议,然后实现协议里面的方法。Block是自己在本类定义了一个代码块,但是在需要的时候才会去调用。下面是一个block使用的小列子:
业务需求是在一个封装的view中点击了tableView的某一行,然后通过block把这一行上的字符串传出去进行处理,下面是具体实现:
view的.h文件:
typedefvoid (^ChooseViewClick)(NSString*);
@interfaceChooseView : UIView<UITableViewDataSource,UITableViewDelegate>
@property(nonatomic,copy)ChooseViewClick chooseClick;
@end
当tableView的某一行被点击时:
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self dismiss];
NSString *str =_showArray[indexPath.row];
if(self.chooseClick) {
self.chooseClick(str);
}
}
在另一个控制器进行处理
_chooseView.chooseClick = ^(NSString *str)
{
_selectTag = 0;
[btnsetTitle:str forState:UIControlStateNormal];
};
4 block使用之避免循环引用
在使用block的时候要特别注意的是避免循环引用,循环引用指的是两个对象都相互强引用了对方,从而导致谁也释放不了,引起内存泄露问题。最常见的分为两种情况,一种是这个对象拥有这个block,但是在block块里面又引用了这个对象本身,第二种是block是宿主的一个属性,但是在block里面又访问了宿主的其他属性。
第一种情况,block是self本身的一个属性,在block里面又访问了宿主本身:
self.myblock = ^{
[self doSomething];
};
在这里会引起循环引用,解决办法是用__weak修饰,正确代码如下:
__weak typedef(self) weakSelf = self;
self.myblock = ^{
[weakSelf doSomething];
};
第二种情况,block是宿主本身的属性,在block里面又访问了宿主的其他属性:
self.myblock = ^{
[self.muArray removeAllObjects];
}; //引起循环引用
此时解决方案跟上面一样,可以加__block 或者__weak 来修饰,正确代码如下:
__block typedef(self) weakSelf = self;
self.myblock = ^{
[weakSelf.muArray removeAllObject];
};