这一节学习新的语法-block代码块。它是在iOS4.0和Mac OS X10.6以后的版本中才添加的,是对C语言的扩展。
简单的理解block代码块的作用就和C语言的函数的作用类似,就是将能够实现某种特定功能的一些代码包裹起来,留出必要的接口(就是参数),供外界调用。
知道block代码块的功能后,学习一下block的语法规则。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
void (^helloWorld)(void);
helloWorld=^(void){
NSLog(@"Hello World!");
};
helloWorld();
return 0;
}
运行结果:Hello World!
可以看到,上边的例子通过使用block代码块实现了“Hello World”小程序。分解一下block代码块,看看到底是怎么实现的。
第4行代码:是block的声明,第一个void是代码块的返回值类型,(^helloWorld):“^”是block代码块的标志,无实际意义。helloWorld是block代码块的名称,必须要用小括号扩起来。第二个void是代码块的参数,如果需要有参数,语法是:(int,……)或(int i,……)都可以;如果不需要参数,可以采用:()或(void)但是不能连括号都不写。
6-8行代码:是block代码块的实现。首先在对代码块实现的时候,“^”是必须存在的,(void)中需要写的是当代码块的参数。有参数时,在这里必须写上具体的每个参数名,如果没有参数,可以写成: (void)或()或干脆省略都可以。后边大括号中的代码表示对helloWorld代码块的具体实现代码。
(提示:代码块的声明和实现可以写在一起)
代码块的调用和C语言的函数调用相同。通过:“代码块名(参数)”的方式。
在使用代码块的时候,对于全局变量,在块内是完全可操作的。但是对于局部变量来说,在块内只能使用不能更改。
如果试图在块内更改局部变量的值,程序会报错,解决的方案是在声明局部变量时添加__block关键字(注意是两个“_”):创建一个工程,在main.m文件中:
#import <Foundation/Foundation.h>
int number=10;
int main(int argc, const char * argv[]) {
__block int i=10;
void(^block)()=^{
NSLog(@"The i is :%d",i);
NSLog(@"The number is :%d",number);
i++;
number++;
};
block();
NSLog(@"The i is :%d",i);
NSLog(@"The number is :%d",number);
return 0;
}
运行结果:
The i is :10
The number is :10
The i is :11
The number is :11
(对于Block代码块的使用,它也可以和C语言中的函数一样,写在main函数的外面,但是必须遵循“先声明,后使用”的原则)
代码块的具体使用
通过上面的学习,应该基本上可以使用代码块了。在实际开发过程中,代码块的使用都出现在那些地方呢?
1、我们在使用Foundation框架中的某些方法时,可能会看到代码块,这些代码块作为方法的参数。例如:对NSArray类的数组进行排序可以使用代码块排序:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
NSArray * array=[NSArray arrayWithObjects:@"4",@"1",@"2",@"3",@"5", nil];
array= [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
int a1=[obj1 intValue];
int a2=[obj2 intValue];
return a1>=a2?-1:1;
}];
NSLog(@"%@",array);
return 0;
}
运行结果:
通过上边对数组对象进行排序的例子,你会发现block代码块作为参数使用时,当使用这个排序方法时,代码块中的变量或对象是有值的。
通过将返回的数值进行比较,然后再返回给这个方法一个结果,就完成了对数组的排序。整个过程用到了block代码块的回调。
下面通过一个例子对这个过程进行一下模拟;
1、首先创建一个工程,例如:Demo;
2、创建一个Person类;
在Person.h中:
#import <Foundation/Foundation.h>
typedef void(^myBlock)(NSString * name,int age);
@interface Person : NSObject
-(void)exercise:(myBlock)block;
@end
其中,typedef时OC中的关键字,表示重命名,作用是通过使用typedef关键字,简化较为复杂的类型声明,在后边使用代码块的时候,直接用代码块的名字代表就可以了。
在Person.h中,首先声明了一个全局的block代码块,并将之作为方法的参数。
在Person.m文件中:
#import "Person.h"
@implementation Person
-(void)exercise:(myBlock)block{
NSString * theName=@"ZhangSan";
int age=10;
block(theName,age);
}
@end
在这个文件中,对.h声明的方法进行了实现,在实现过程中,我们调用了block代码块。直到现在我们还没有对block代码块做实现。
下面来看一下main.m文件中的代码:
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
Person * person=[[Person alloc] init];
[person exercise:^(NSString *name, int age) {
NSLog(@"%@,%d",name,age);
}];
return 0;
}
在main.m文件中,我们看到,我们对block代码块做了实现,输出name值和age的值。通过给工程打断点,我们会看到这段小程序的运行过程:
首先,初始化一个person对象向person对象发送exercise:消息
运行person.m文件中的exercise实现部分的代码
运行到block代码块时,运行代码块的实现部分,也就是main.m文件中的输出语句
block代码块的实现部分运行完后,程序继续运行exercise方法的实现部分。
exercise方法的实现部分运行完后,表明向person对象发送的消息就完成了
然后运行return 0结束程序。
通过跟踪程序的执行顺序,我们可以看到block代码块作为参数时的程序运行流程。正是利用block代码块的这种运行机制,我们可以利用block完成传值的功能。
block代码块,实际开发过程中,对于自定义的代码块很少用到,但是在Foundation框架中,block代码块作为方法参数的情况比较多。
而且,虽然协议、通知等都有传值的功能,但是如果你能够灵活运用block代码块,比起前两种方式编程将变得更简单。