Block学习

来源:互联网 发布:天猫红包怎么领取淘宝 编辑:程序博客网 时间:2024/06/07 00:09

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.

//测试1

/*Block对象是一段可执行的代码,可以像调用函数那样调用

声明并创建一个Block对象,声明的时候可以忽略形参名
Block对象必须指向跟它类型匹配的Block对象,返回类型和参数类型必须一致
Block对象是在方法内或者函数内创建的,实现文件中要调用某个Block对象,

就必须有一个指向它的指针对象

*/
//返回值为int,Bloc对象的名字为adder,有两个int类型的参数
int (^adder)(int,int) = ^int(int x,int y)
{
return x + y;
};

//执行Block对象
int sum = adder(2,5);
NSLog(@"===Block test1===%d",sum);

//测试结果:===Block test1===7

/*
如果某个对象的某个方法可以接受两个数字并返回相加的结果,固然可以用函数实现。
但是如果要实现加减乘除的时候就不得不不断地给这个对象添加新的方法。

用Block能很好的解决这个问题。只需要在这个类中声明一个Block对象,一个可以接受参数的方法。
在实际用的过程中,我们只需要将不同的Block对象传给这个类的Block对象即可。


见下面的例子!
*/
//测试2
Executor * executor = [[Executor alloc]init];
//加
[executor setEquation:adder];
NSLog(@"===Block test2 加法===%d",[executor computeWithValue:3 andValue:8]);

//减法
int (^subtraction)(int,int) = ^int(int x,int y)
{
return x - y;
};
[executor setEquation:subtraction];
NSLog(@"===Block test2 减法===%d",[executor computeWithValue:3 andValue:8]);

//乘法
int (^multiplication)(int,int) = ^int(int x,int y)
{
return x * y;
};
[executor setEquation:multiplication];
NSLog(@"===Block test2 乘法===%d",[executor computeWithValue:3 andValue:8]);


//除法
int (^division)(int,int) = ^int(int x,int y)
{
return x / y;
};
[executor setEquation:division];
NSLog(@"===Block test2 除法===%d",[executor computeWithValue:3 andValue:8]);

/*
结果:
===Block test2 加法===11
===Block test2 减法===-5
===Block test2 乘法===24
===Block test2 除法===0
*/


//test3

/*

Block对象有一个很有用的特性---捕获变量
Block对象可以使用传入的实参,也可以声明局部变量,可以使用其作用域内的所有变量。
[对声明了某个Block对象的方法,该方法的作用域就是这个Block对象的作用域。]
因此Block对象可以访问它所在方法中的所有局部变量、传入的参数已经当前对象的实例变量。

当某个Block对象访问了在该Block对象声明之前的变量时,可以称该Block对象捕获了这个变量。

*/

int temp = 5;
[executor setEquation:^int(int x,int y)
{
int sum = x + y;
return temp * sum;//捕获Block声明之前的temp变量,会将temp的值拷贝至Block对象的内存中
}];
//在Block捕获temp变量之后,在调用Block之前又修改了temp的值
temp = 10;
NSLog(@"===test Block对象捕获变量特性:%d",[executor computeWithValue:3 andValue:8]);

/*
结果:===test Block对象捕获变量特性:55

说明:一旦Block对象捕获了某个变量,那么该变量之后的变化就不会对捕获后的变量产生任何影响。
*/

//test4
/*
每个IOS应用都有一个主操作队列NSOperationQueue,该队列中的操作都将成为运行循环中的事件,

每次循环弹出一个。
得到指向主操作队列的指针,然后为该队列添加一个Block对象。加入主操作队列的Block不能有返回值,

也不能有参数。
*/
[[NSOperationQueue mainQueue]addOperationWithBlock:^void(void){
NSLog(@"===test 将Block加入主操作队列:%d",[executor computeWithValue:3 andValue:8]);
}];

/*
结果:
exiting method!
===test 将Block加入主操作队列:55
Executor is Being destoryed!
顺序:包含Block对象的方法结束、主操作队列中的Block对象得到执行、释放Block对象。

说明:Block对象可以捕获任意类型的变量,包括对象的指针。因此可以在Block对象中向某个对象发送消息。
这个输出顺序说明:Block对象会针对其作用域内使用的对象,保留强引用类型的引用。

因此,当Block对象向某个对象发送消息或者使用某个对象,都会导致应用保留该对象,直到释放该Block对象。
*/
executor = nil;

self.viewController = [[LMViewController alloc] initWithNibName:@"LMViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];

NSLog(@"exiting method!");
return YES;

/*
__block、简化语法与内存管理
1、创建Block时返回值不是必须的。编译器会根据Block对象的return语句,自动判断返回类型。
int (^block)(void) = ^(void){
return 10;
};

NSString * (^block)(void) = ^(void)
{
return [NSString stringWithString:@"xx"];
};

2、如果某个Block对象没有实参,就可以在创建该对象时忽略参数列表。
void(^block)(void) = ^{
NSLog(@"hehe");
};

3、虽然在创建Block对象时可以省略返回值和参数列表,但是在声明Block对象时都不能省略。

4、如果某个变量被Block对象捕获后并且被copy到了Block对象的内存空间中。

那么Block对象中不能修改该变量。
int i = 10;
void (^block)(void) = ^
{
i = 15;//会报错:Variable is not assignable(missing __block type specifier)
};

5、用关键字__block修饰的变量可以在被Block捕获后进行修改。但是,这类变量既不在当前的栈中,

也不在Block对象的栈中,而是在一块特殊的内存空间中,这就意味着可以有多个Block对象修改

同一个__block变量。
__block int count = 0;

void(^pluseOne)(void) = ^{
count ++;
NSLog(@"count=%d",count);
};
void(^pluseTwo)(void) = ^
{
count +=2;
NSLog(@"count=%d",count);
};
pluseOne();//1
pluseTwo();//3
pluseOne();//4

6、如果定义Block对象后修改了其捕获的__block修饰的变量,那么该Block中的变量值也会发送改变。
__block int count = 1;
void(^block)(void) = ^{
NSLog(@"count=%d",count);
};
count = 10;
block();//10

7、如果Block对象捕获的是一个指向对象的指针变量,那么不需要__block修饰该变量,

Block中也一样可以修改该变量的属性(但是不能修改其指向的对象)。
如果用__block修饰该变量,那么也可以在Block中修改该变量所指向的对象。

NSString * str = @"hehe";
__block NSString * str1 = @"he";
void (^block)(void) = ^
{
str1 = @"hehe";
str = @"he"; //会报错:Variable is not assignable(missing __block type specifier)
};

8、应用会将新建的Block对象保存在栈中,即使针对新创建的Block对象保留强引用,

一旦该对象的方法返回,新创建的Block对象就一样会被释放。
通过向Block对象发送copy消息,应用会在堆中创建该对象的备份。
如果收到copy消息的Block对象已经位于堆中的拷贝,就只会增加一个指向该对象的强引用,不会重复拷贝。

*/
}

//Executor.h


#import <Foundation/Foundation.h>

@interface Executor : NSObject
{
//一个返回值为int带两个int参数的名为equation的Block对象
int (^equation)(int ,int);
}
//和其他实例变量一样,可以将Block对象声明为类的属性
//通过这个方法可以为Executor设置block对象
@property (nonatomic,copy) int(^equation)(int,int);
//通过这个方法可以给Executor的block对象传递两个int参数
-(int) computeWithValue:(int)value1 andValue:(int)value2;

@end


//Executor.m


#import "Executor.h"

@implementation Executor

//通过这个方法可以为Executor设置block对象
@synthesize equation;

//通过这个方法可以给Executor的block对象传递两个int参数
-(int) computeWithValue:(int)value1 andValue:(int)value2
{
//如果某个Block对象没有指向任何Block对象,执行该Block对象会奔溃,所以要检查
if(!equation)
{
return 0;
}
//向Block对象传入value1和value2返回计算结果
return equation(value1,value2);
}

//重写dealloc方法,当对象被释放的时候输出一句话
-(void)dealloc
{
NSLog(@"Executor is Being destoryed!");
}
@end


参考书:《ios编程》第三版


0 0