IOS ——OC—— 代码块的理解
脱字符(^)是块的语法标记。按照我们熟悉的参数语法规约所定义的返回值以及块的主体(也就是可以执行的代码)。下图是如何把块变量赋值给一个变量的语法讲解:
按照调用函数的方式调用块对象变量就可以了:
int result = myBlock(4); // result是 28
1、参数是NSString*的代码块
- void (^printBlock)(NSString *x);
- printBlock = ^(NSString* str)
- {
- NSLog(@"print:%@", str);
- };
- printBlock(@"hello world!");
运行结果是:print:hello world!
2、代码用在字符串数组排序
- NSArray *stringArray = [NSArray arrayWithObjects:@"abc 1", @"abc 21", @"abc 12",@"abc 13",@"abc 05",nil];
- NSComparator sortBlock = ^(id string1, id string2)
- {
- return [string1 compare:string2];
- };
- NSArray *sortArray = [stringArray sortedArrayUsingComparator:sortBlock];
- NSLog(@"sortArray:%@", sortArray);
运行结果:sortArray:(
"abc 05",
"abc 1",
"abc 12",
"abc 13",
"abc 21"
)
3、代码块的递归调用
代码块想要递归调用,代码块变量必须是全局变量或者是静态变量,这样在程序启动的时候代码块变量就初始化了,可以递归调用
- static void (^ const blocks)(int) = ^(int i)
- {
- if (i > 0) {
- NSLog(@"num:%d", i);
- blocks(i - 1);
- }
- };
- blocks(3);
运行打印结果:
num:3
num:2
num:1
4、在代码块中使用局部变量和全局变量
在代码块中可以使用和改变全局变量
- int global = 1000;
- int main(int argc, const char * argv[])
- {
- @autoreleasepool {
- void(^block)(void) = ^(void)
- {
- global++;
- NSLog(@"global:%d", global);
- };
- block();
- NSLog(@"global:%d", global);
- }
- return 0;
- }
运行打印结果:
global:1001
global:1001
而局部变量可以使用,但是不能改变。
在代码块中改变局部变量编译不通过。怎么在代码块中改变局部变量呢?在局部变量前面加上关键字:__block(前面是两个下划线)
例:
- __block int local = 500;
- void(^block)(void) = ^(void)
- {
- local++;
- NSLog(@"local:%d", local);
- };
- block();
- NSLog(@"local:%d", local);
运行结果:local:501
代码块
代码块对象(简称为代码块)是对C语言中函数的扩展。除了函数中的代码,代码块还包含变量绑定。代码块有时也被称为闭包(closure)。
代码块包含两种类型的绑定:自动型和托管型。自动绑定使用的是栈中的内存,而托管绑定使用的是通过堆创建的。
因为代码块实际上是由C语言实现的,所以他们在各种以C作为基础的语言内都是有效的,包括Objective-C、C++以及Objective-C++。
代码块在Xcode的GCC和Clang工具中是有效的,但它不属于ANSI的C语言标准。
代码块和函数指针
代码块借鉴了函数指针的用法。所以如果你知道如何声明函数指针,也就知道如何声明一个代码块。与函数指针类似,代码块具有以下特征:
- 返回类型可以手动声明也可以由编译器推倒;
- 具有指定类型的参数列表;
- 拥有名称。
来看一下函数指针的声明。如下:
void (*my_func)(void);
void (^my_func)(void);
int (^square_block)(int number) =
^(int number) {return (number * number);};
int result = square_block(5);
printf(“Result = %d\n”, result);
<returntype> (^blockname)() = ^{printf("Hello Blocks!\n");};
使用代码块
如果你将代码块声明成了一个变量,所以可以像函数一样使用它。如上面:int result = square_block(5); 这行代码并没有幂符号,因为只有在定义代码块的时候才需要使用它,调用时则不需要。
代码块有一个非常酷的特性可以替换原先的函数:代码块可以访问与它相同的(本地)有效范围内声明的变量,也就是说可以访问与它同时创建的有效变量。
int value = 6;
int (^multiply_block)(int number) = ^(int number)
{
return (value * number);
}
int result = multiply_block(7);
printf(“Result = %d\n”, result);
直接使用代码块
使用代码块的时候通常不需要创建一个代码块变量,而是在代码中内联代码块的内容。通常,你会需要一个代码块作为参数的方法或函数。
NSArray *array = [NSArray arrayWithObjects: @“Amir”, @“mishal”, @“Irrum”, @“Adam”, nil];
NSLog(@“Unsorted Array %@”, array);
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(NSString *object1, NSString *object2) {
return [object1 compare: object2];
}];
NSLog(@“Sorted Array %@”, sortedArray);
使用typedef关键字
上面长的变量定义语句让人看的有点晕。我们可以用typedef关键字。
typedef double (^MKSampleMultiply2BlockRef)(double c, double d);
MKSampleMultiply2BlockRef multiply2 = ^(double c, double d)
{
return c * d;
}
printf(“%f, %f”, mutiply2(4, 5), mutiply2(5, 2));
代码块和变量
代码块被声明后会捕捉创建点时的状态。代码块可以访问函数用到的标准类型的变量。
- 全局变量,包括在封闭范围内声明的本地静态变量
- 全局函数
- 封闭范围内的参数
- 函数级别(即,与代码块声明时相同级别)的_block变量。它们是可以修改的变量。
- 封闭范围内的非静态变量会被获取为常量。
- Objective-C的实例变量。
- 代码块内部的本地变量。
本地变量
本地变量就是与代码块在同一范围内声明的变量。我们看下面的一个示例:
typedef double (^MKSampleMultiplyBlockRef)(void);
doub a = 10, b = 20;
MKSampleMultiplyBlockRef multiply = ^(void) {return a * b};
NSLog(@“%@”, multiply());
a = 20;
b = 50;
NSLog(@“%@”, multiply());
全局变量
在上面的示例中,我们说过变量与代码块拥有相同的有效范围,可以根据需要把变量标记为静态的(全局的)。
static doub a = 10, b = 20;
MKSampleMultiplyBlockRef multiply = ^(void) {return a * b};
NSLog(@“%@”, multiply());
a = 20;
b = 50;
NSLog(@“%@”, multiply());
参数变量
代码块中的参数变量具有和函数中的参数变量相同的作用。
typedef double (^MKSampleMultilply2BlockRef)(double c, double d);
MKSampleMultiply2BlockRef multiply2 = ^(double c, double d)
{
return c * d;
}
NSLog(@“%f, %f”, mutiply2(4, 5), mutiply2(5, 2));
_block变量
本地变量会被代码块作为常量获取到。如果你想要修改它们的值,必须将它们声明为可修改的。否则,像下面这样,会出现出现错误。
double c = 3;
MKSampleMultiply2BlockRef multiply = ^(double a, double b){ c = a * b; };
_block double c = 3;
MKSampleMultiply2BlockRef multiply = ^(double a, double b){ c = a * b; };
- 没有长度可变的数组;
- 没有包含可变长度数组的结构体。
代码块内部的本地变量
void fun(int (^block)(int a,int b))
{
int sum1 = block(1,2);//代码块的调用
NSLog(@"sum1=%d",sum1);
}
//NSString 类型的代码块作为fun1函数的形参
void fun1(NSString *(^myBlock)(NSString *s,NSString *s1)){
NSLog(@"调用fun1后的结果:%@",myBlock(@"41651",@"46515"));//myBlock(@"41651",@"46515")相当于代码块的调用
}
void fun2(NSString *(^myBlock)(NSString *s),NSString *s1){
NSLog(@"————%@",myBlock(s1));
}
int c = 0;//全局变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 声明格式:
//返回值类型(^black名字)(形参列表)
int (^myfun)();
//block实现:
//block 名字 = ^(形参列表){ }
myfun = ^()
{
return 1;
};
//调用:
//block名称(实参列表)有返回值的block,可以用一个变量进行接收;
int a = myfun();
NSLog(@"a=%d",a);
//有返回值,有形参,声明和实现放在一起;
int (^myblock)(int a,int b)=^(int a,int b)
{
return 1+b;
};
//调用
int sum = myblock(10,20);
NSLog(@"sum=%d",sum);
//返回值类型是NSString的代码块
NSString *(^myblock1)(NSString *s) = ^(NSString *s)
{
NSLog(@"字符串s为:%@",s);
return s;
};
myblock1(@"4865");
//有一个局部变量,要在block中进行值的改变
__block int b = 0;//声明代码块局部变量的方式;
void (^myblock2)() = ^()
{
b++;
};
myblock2();
NSLog(@"b=%d",b);
//有一个全局变量,在block进行值的改变
void (^myBlock3)() = ^(){
c++;
};
myBlock3();
NSLog(@"c=%d",c);
//block最为函数的形参的调用
//当一个block作为函数的参数时,其返回值类型,形参个数及类型要与函数形参格式保持一致;
int (^myblock4)(int c,int d)= ^(int a,int b)
{
return a+b;
};
//(第一次调用fun)函数的形参是block类型,调用时,直接传block的名字。
fun(myblock4);//fun函数调用;
//(第二次调用fun)内联block格式:^返回值类型(形参列表){}
fun(^int(int a,int b)
{
return a*b;
});//可以用这种方法代替上面第一种调用的方法;
//使用内联block调用fun1函数
fun1(^NSString *(NSString *s, NSString *s1) {
NSString *str = [s stringByAppendingString:s1];//代码块的作用实现两个字符串的拼接;
return str;//代码块的返回值
});//()里面的内容相当于fun1的实参{}里面的内容相当于代码块的功能;
fun2(^NSString *(NSString *s) {
NSRange range = NSMakeRange(0, 3);
NSString *str1 = [s substringWithRange:range];
return str1;
sum=21
字符串s为:4865
b=1
c=1
sum1=3
sum1=2
调用fun1后的结果:4165146515
//使用typedef声明block: typedef 返回值类型 (^名字)(形参列表)
typedef int (^myblockType)(int a);
@interface Person : NSObject
@property(nonatomic ,strong)NSString *name;
//block作为方法的参数:
//(返回值及类型(^)(形参列表))参数名(block名字)
//其中形参列表中可以 只有形参类型名没有形参变量
-(void)myblock:(int (^)(int a))block;
-(void)sengstr:(NSString *)name andblock:(NSString *(^)(NSString *))myblock;
-(void)useblockType:(myblockType)sss;
@implementation Person
-(void)myblock:(int (^)(int))block
{
int b = block(30);//代码块的实现
NSLog(@"b=%d",b);
}
-(void)sengstr:(NSString *)name andblock:(NSString *(^)(NSString *))myblock//name是一个参数myblock是一个参数
{
myblock(name);
}
-(void)useblockType:(myblockType)sss//将代码(myblockType)块看做是int
{
int f = sss(2);
NSLog(@“f=%d",f);
}
#import"Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc]init];
int (^youblock)(int d) = ^(int g)
{
return g*7;
};//定义一个代码块形式的参数
[p myblock:youblock];//将代码块作为参数传给myblock方法实现myblock方法的调用;
[p myblock:^int(int a) {
return a+6;
}]; //使用内联block直接调用myblock方法
[p sengstr:@"王老五" andblock:^NSString *(NSString *s) {
NSLog(@"我叫:%@",s);
return s;
}];
[p useblockType:^int(int c) {
return c*5;
}];
}
return 0;