block块是一种语法,里边封装了用户自定义的代码块,用来实现某些方法功能。
block的定义方式:
与函数指针的定义很像,只是把函数指针的*号改成 ^ 号。
先写^,然后写返回值类型,接着是参数类型,参数名,然后是大括号,分号,大括号里的代码。
/* //定义Block //返回值可以自己推断出来是什么类型的。 ^int(int num1,int num2){ return num1 + num2; }; |
解释:
^ :block的标志;
第一个int:返回值类型;
int num1:第一个参数类型和参数名;
int num2:第二个参数类型和参数名;
{ } 里是我们想实现的某些功能,不能少了分号。
一些练习:
//练习 //无参无返回值,打印hello world ^void(){ NSLog(@"hello World"); }();//在此加上小括号()就是Block调用
^(){ NSLog(@"hello world"); }; //无参有返回值,返回一个字符串NSString ^NSString *(){ return @"hello world"; };
^(){//会自动根据return返回的东西进行判断返回值是什么类型的。 return @"hello world"; }; |
block的调用,可以在定义的时候,在大括号{}加一个(),()里边是传递的参数。
block的定义,返回值类型可以省略,因为可以根据block里的返回值,自动判断返回值的类型。但是参数类型和参数名不能省略。
block变量来保存block块
与指针函数一样,我们把指针函数的方式定义block变量,但是要把* 号改成 ^号。方式如下:返回值 + (^变量名)+(参数类型和个数) = block块
//定义一个BLock变量,保存Block块 // = 号前面是一个变量(类似指针函数,只是把括号里的*号改成^号即可) //类型不要加括号 void (^b)() = ^void(){ NSLog(@"Hello,world"); };
int (^b11)(int,int) = ^int(int num1 ,int num2){ return num1 +num2; };
NSString *(^b3)() = ^NSString *(){ return @"hello world"; }; |
block的调用
block的调用,就是block定义变量后,用变量名+括号(),括号里放参数,与C语言的方法调用很相似。
//Block调用,使用Block变量加上括号(),里边放参数。 int i = b11(1, 1); NSLog(@"%d",i); b(); NSLog(@"%@",b3()); |
练习:定义一个block,实现把字符串转换成整型数。
//练习 int (^b22)(NSString *) = ^int (NSString *s){ int num = [s intValue]; return num; }; NSLog(@"%d", b22(@"999999994"));//调用block |
练习:定义一个block,实现数组排序
//练习 //Block,返回值Nsarray,参数NSArray(都是NSString),功能,按升序排。 NSArray *(^sort)(NSArray *) = ^NSArray *(NSArray *arr){
NSArray *temp = [arr sortedArrayUsingSelector:@selector(compare:)];
return temp; }; NSArray *temp1 = [[NSArray alloc]initWithObjects:@"f",@"b",@"c",@"d",@"e", nil]; NSArray *arr = sort(temp1); NSLog(@"%@",arr); |
练习:定义block实现比较两个数的大小:
//block 返回值 int,参数两个int //功能,返回两个数最大值 int (^max)(int ,int ) = ^int (int num1, int num2){ return num1 > num2? num1:num2; }; int num = max(311,5); NSLog(@"%d",num); |
练习:返回3个数乘积
//block 返回值:float类型,参数3个float //功能:求三个数的乘积
float (^mul)(float ,float,float) = ^float (float num1,float num2,float num3){ return num1 * num2 *num3; };
float n = mul(7,8,9); NSLog(@"%.2f",n); |
typedef给block类型取一个新名
与指针函数一样,用typedef给block取一个名字,方式与函数指针一样,只要把 * 号改成 ^号即可。就可以直接使用新名字定义一个变量。
#import <Foundation/Foundation.h> //typedef //typedef int(^NewBlock)(int ,int );^写在这里也行 //typedef int(NewBlock)(int ,int );//^不写在这里也行,那就得写在定义的时候 int main(int argc, const char * argv[]) { @autoreleasepool { //typedef NewBlock nb = ^int (int num1 ,int num2 ){ NewBlock ^nb = ^int (int num1 ,int num2 ){//^号写在nb这里 return num1 + num2; }; int i = nb(12,23); NSLog(@"%d",i);//2015-04-20 16:32:36.069 OCLesson6_Block[2260:131999] 35 } return 0; } |
练习:
#import <Foundation/Foundation.h> typedef int (StringToInt) (NSString *); int main(int argc, const char * argv[]) { @autoreleasepool { StringToInt ^sti = ^int (NSString *str){ return [str intValue]; }; int num = sti(@"2222333"); NSLog(@"%d",num); } return 0; } |
局部变量
局部变量在block里可以读取,但是不能改值。所以,如果想改变一个变量的值,有三种方法:
1、把局部变量放到方法外面,使之成为全局变量
2、在方法里,定义的局部变量,用双下划线block来声明:__block(下划线有两条)。
3、在方法里定义的局部变量,用static修饰。
如果局部变量是指针的话,在block里,可以改值,但是改得值是这个指针指向的堆中的地址里的值,不是改变这个指针本身的值,所以是这个指针根本就没有变化,变化的是指针所指向的地址里的值。
int i = 10;//局部变量 // static int i = 10;//静态修饰局部变量 __block int i = 10;//声明是__block,即把变量放到静态区了。 VoidBlock ^vb = ^void (){ //局部变量的值block块中,可以访问。但是不能改值。 //如果想改变变量有3种方式: //1、变量变为全局变量(把变量定义放到函数外面) //2、使用下划线下划线block( __block )声明的变量(两个下划线) //3、static修饰局部变量 i = 110; NSLog(@"%d",i);//2015-04-20 16:49:48.813 OCLesson6_Block[2397:137508] 10 }; NSLog(@"%d",i);//2015-04-20 16:50:14.376 OCLesson6_Block[2406:137717] 110 vb(); NSLog(@"%d",i);//2015-04-20 16:50:39.051 OCLesson6_Block[2415:137950] 110 */ /* NSMutableArray *array = [[NSMutableArray alloc]init];
VoidBlock (^vb2) = ^void (){ //这个时候,array是指针,保存地址,地址没有改变,改变的是array所指向的堆区的值。所以没有错误。 [array addObject:@"2"]; //打印添加元素时的地址 NSLog(@"%p",array);//2015-04-20 17:05:02.246 OCLesson6_Block[2480:142637] 0x10030cb90 };
vb2(); NSLog(@"%@",array); // 2015-04-20 17:00:25.266 OCLesson6_Block[2452:141334] ( // 2 // ) //打印添加元素后的地址 NSLog(@"%p",array);//2015-04-20 17:05:02.246 OCLesson6_Block[2480:142637] 0x10030cb90 |
block作为函数参数
这个设计很妙,就是很多时候,我们是得不到.m这个实现方法的文件的,我们只有.h文件里声明的接口。此时我们想改变或者添加一些功能,不能在.m文件里改,只能在我们使用的方法里增加一些代码。此时,就可以在block里添加我们想要实现的功能,然后把这个block当做参数传入.m中。事先,我们在.m中如果设置了调用block的方法,那么我们把block传入这个方法,就可以执行我们传入的block里边的代码块,实现功能。其实与函数指针作为函数参数一样,把指针传入某个方法里,然后这个方法执行的时候,是去传入的指针所指向的函数执行那个函数。
Person.h
#import <Foundation/Foundation.h> typedef void (^MyBlock)(); @interface Person : NSObject{ NSString *_name; NSInteger _age; }
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
- (NSString *)name; - (NSInteger)age;
- (void )setName:(NSString *)name; - (void )setAge:(NSInteger)age;
//将 block作为参数传入()其实就是一段代码传入 - (void)sayWhat:(MyBlock )mb; @end |
Person.m
#import "Person.h" @implementation Person //将 block作为参数传入()其实就是一段代码传入 - (void)sayWhat:(MyBlock )mb{ //假设有一段执行代码 //调用block()其实就是在执行传进来的那一段代码。 mb(); //执行代码 }
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age{ self = [super init]; if (self) { _name = name; _age = age; } return self; }
//getter - (NSString *)name{ return _name ; } - (NSInteger)age{ return _age; } //setter - (void )setName:(NSString *)name{ _name = name; } - (void )setAge:(NSInteger)age{ _age = age ; } - (NSString *)description { return [NSString stringWithFormat:@"name = %@,age = %ld", _name,_age]; }
@end |
main.m
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { // Person *p = [[Person alloc]init]; // //这个时候,这个block(这段代码作为参数传入方法中)。 // [p sayWhat:^void(){ // // NSLog(@"hello bobo"); // // }]; } return 0; } |
执行过程,在main.m和Person.m打断点即可知道
练习:Person.h和Person.m跟上面的一样。
main.m
//练习 Person *p1 = [[Person alloc]initWithName:@"AAA" age:18]; Person *p2 = [[Person alloc]initWithName:@"CCC" age:12]; Person *p3 = [[Person alloc]initWithName:@"BBB" age:19];
NSArray *sortArr = [NSArray arrayWithObjects:p1,p2,p3, nil]; //1.按照年龄升序 NSArray *sorted = [sortArr sortedArrayUsingComparator:^NSComparisonResult(Person *obj1,Person *obj2){ if (obj1.age > obj2.age) { return NSOrderedDescending; }else if(obj1.age == obj2.age){ return NSOrderedSame; }else{ return NSOrderedAscending; } }]; NSLog(@"%@",sorted); |