先瞧瞧几个例子:
Example 1(Block 在动画方面的应用):
- daysView.transform = CGAffineTransformMakeScale(0.1f, 0.1f); [UIView animateWithDuration: 0.2f delay: 0.0f options: UIViewAnimationCurveEaseInOut animations: ^{
- daysView.alpha = 1.0f;
- daysView.transform = CGAffineTransformMakeScale(1.05f, 1.05f);
- }
- completion: ^(BOOL finished) { [UIView animateWithDuration: 0.2f delay: 0.0f options: UIViewAnimationOptionCurveEaseInOut animations: ^{
- daysView.transform = CGAffineTransformIdentity;
- daysView.frame = CGRectMake(point.x - 150.0 / 2, point.y + 10.0, 150, 100);
- }
- completion: nil];
- }];
Example 2(Block 在多线程方面的应用):
- dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_async(concurrentQueue, ^{
- __block UIImage * image = nil;
- dispatch_sync(concurrentQueue, ^{
- /* Download the image here */
- });
- dispatch_sync(dispatch_get_main_queue(), ^{
- /* Show the image to the user here on the main queue*/
- });
- });
Example 3(Block 在单例方面的应用):
- + (id) sharedInstance {
- static id sharedInstance;
- static dispatch_once_t once;
- dispatch_once( & once, ^{
- sharedInstance = [[[self class] alloc] init];
- });
- return sharedInstance;
- }
Example 4(Block 在网络请求方面的应用):
- __block MKNetworkOperation * op = [engine operationWithURLString: urlString params: dic httpMethod: @"POST"]; [op addCompletionHandler: ^(MKNetworkOperation * completedOperation) {
- NSString * responseString = [completedOperation responseString];
- NSLog(@"communication requestFinished");
- completeCallBack(responseString);
- }
- errorHandler: ^(MKNetworkOperation * completedOperation, NSError * error) {
- NSLog(@"error:%@", error);
- completeCallBack(kHttpRequestError);
- }]; [engine enqueueOperation: op];
What
iOS4 引入了一个新特性,block(也称块代码)。
block 可以简单看做是一组可执行的代码,block 能够捕捉到已声明的同一作用域的变量。
block 也是闭包,在代码块声明时就将使用的变量包含到代码块范围内。
block 还是 Objective-C 对象,我们可以像对象一样传递它们。
Block 可以有返回值,可以接受参数。还可以内敛定义,当做独立代码块,与传统 C 函数类似。
Why
block 普遍被认为是实现 Callback 的一种更为方便的模式,相比 Delegate+Protocol 需要声明和实现一大推方法而言,block 表现得更为灵活。
它们可以让你在调用的地方编写代码实现后面将要执行的操作。因此 blocks 通常作为框架方法的参数。
它们允许你访问局部变量。而不需要集成所有上下文的信息的数据结构来进行回调,你可以简单的访问局部变量。
Block 编写函数表达式,这些表达式可以作为 API 使用,或可选的存储,或被多个线程使用。
iOS4 在 UIKit 中引入了该特性,此后越来越多的 Apple API 都使用了 block,所以我们应该了解这一知识。
How1、Block 的写法
假设你在 Objective-C 中有个方法,既能接受 NSInteger 类的 2 个整数值,又能通过两个相减返回两者的差值作为 NSInteger(可以这么写):
- - (NSInteger) subtract: (NSInteger) paramValue from: (NSInteger) paramFrom {
- return paramFrom - paramValue;
- }
把 Objective-C 代码翻译成纯粹 C 函数变成:
- NSInteger subtract(NSInteger paramValue, NSInteger paramFrom) {
- return paramFrom - paramValue;
- }
如果是代码块又该如何编码?
- NSInteger( ^ subtract)(NSInteger, NSInteger) = ^(NSInteger paramValue, NSInteger paramFrom) {
- return paramFrom - paramValue;
- };
2、各种不同情况的 Block
普通情况下使用 block
- NSInteger count = 10;
- NSInteger( ^ theBlock)(NSInteger) = ^(NSInteger num) {
- return count * num;
- };
- NSLog(@"count * num: %d", theBlock(10));
结果输出:BlockTest[1037:11303] count * num: 100
那么,
1、block 可以使用相同作用域范围内定义的变量。
2、如果你声明一个 block 作为变量,可以把它当成函数来使用。
一个内联(inline)的 block
- dispatch_queue_t mainQueue = dispatch_get_main_queue();
- dispatch_async(mainQueue, ^(void) { [[[[UIAlertView alloc] initWithTitle: @"Inline-Block"message: @"Block is amazing!"delegate: nil cancelButtonTitle: @"OK"otherButtonTitles: nil, nil] autorelease] show];
- });
那么,
1、很多情况下,你不需要去声明一个 block 变量,可以简单的写一个内联 block。
2、可以看到 dispatch_async 函数的最后一个参数为 block。
在 Cocoa frameworks 下的 block
- NSDictionary * femaleStars = [NSDictionary dictionaryWithObjectsAndKeys: @"Fan BB", @"First", @"Liu SS", @"second", @"Liu Yifei", @"Third", @"Lin Zhiling", @"fourth", nil]; [femaleStars enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL * stop) {
- NSLog(@"key : %@ , value : %@", key, obj);
- }];
结果输出:
- BlockTest[1760 : 11303] key: First,
- value: Fan BBBlockTest[1760 : 11303] key: second,
- value: Liu SSBlockTest[1760 : 11303] key: Third,
- value: Liu YifeiBlockTest[1760 : 11303] key: fourth,
- value: Lin Zhiling
那么,
1、在 Cocoa frameworks 里面有部分方法使用 block 作为参数
2、一般是在操作完成的时候作为回调使用
3、声明和创建 block
Block 的声明和函数指针的声明类似,block 使用 ^ 修饰符代替 * 修饰符,block 类型可完全操作其他 C 系统类型:
- void( ^ blockReturningVoidWithVoidArgument)(void);
- int( ^ blockReturningIntWithIntAndCharArguments)(int, char);
- void( ^ arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
Blocks 被设计成类型安全的,你可以把一个 block 引用强制转换成任意类型的指针。
在文件级别,你可以把 block 作为全局标示符:
- #import int GlobalInt = 0;
- int( ^ getGlobalInt)(void) = ^{
- return GlobalInt;
- };
———
+ (void)doSomethingWithIndex:(NSInteger)index block:(NSInteger(^)(NSInteger,NSInteger))block;
第一眼看上去,不容易理解。
所以为了提高可读性和避免在. h 和. m 中出现重复,使用 typedef 修改。
typedef NSInteger(^MyJob)(NSInteger,NSInteger);
+ (void)doSomethingWithIndex:(NSInteger)index block:(MyJob)block;
类方法的具体实现:
+ (void)doSomethingWithIndex:(NSInteger)index block:(MyJob)block{
- NSInteger result = 0;
- for (int i = 0; i < index; i++) {
- result = block(i, pow(10, i)) + result;
- }
- NSLog(@"result:%d", result);
- }
Block 的创建和实现:
- [NDBlockTest doSomethingWithIndex: 3 block: ^(NSInteger num1, NSInteger num2) {
- return num1 + num2;
- }];
4、Block 与变量的关系
全局变量可访问,包括在相同作用域范围内的静态变量。
传递给 block 的参数可访问。
属于同一作用域范围的堆 (非静态的) 变量作为 const 变量(即只读)。
属于同一作用域范围内并被__block 存储修饰符标识的变量作为引用传递是可变的。
同一作用域范围内给定的多个 block 可以同时使用一个共享变量。
在独立的块代码中,你不能直接访问 self,如果想访问,把 Object 作为参数传递给块代码。
在独立的块代码中,你不能使用 dot notatiion(点表达式)读写一个已声明属性。
在内联 block 中,有一条重要规则:内联 block 在其方法作用域范围内会为这些变量复制值。
全局变量可访问,包括在相同作用域范围内的静态变量。
传递给 block 的参数可访问。
- const int number = 10; + (void) blockTest4 {
- int x = 123;
- void( ^ printXAndY)(int) = ^(int y) {
- printf("result : %d %d %d\n", x, y, number);
- };
- printXAndY(100);
- }
输出结果:result : 123 100 10
属于同一作用域范围的堆 (非静态的) 变量作为 const 变量(即只读)。
属于同一作用域范围内并被__block 存储修饰符标识的变量作为引用传递是可变的。
同一作用域范 围内给定的多个 block 可以同时使用一个共享变量。
- int x = 123;
- void( ^ printXAndY)(int) = ^(int y) {
- x = x + y; //error printf("result : %d %d %d\n", x, y, number);};printXAndY(100);
———
- __block int x = 123;
- void( ^ printXAndY)(int) = ^(int y) {
- x = x + y; //true printf("result : %d %d %d\n", x, y, number);};printXAndY(100);
在独立的块代码中,你不能直接访问 self,如果想访问,把 Object 作为参数传递给块代码。
在独立的块代码中,你不能使用点表达式读写一个已声明属性。
- void( ^ viewTest1)(NSInteger) = ^(NSInteger num) {
- self.editing = YES; //error};void (^viewTest2)(id) = ^(id self) { [self.view setBackgroundColor:[UIColor redColor]];//error};void (^viewTest3)(id) = ^(id self) { [[self view] setBackgroundColor:[UIColor redColor]];//true};
在内联 block 中,有一条重要规则:内联 block 在其方法作用域范围内会为这些变量复制值。
- (void)scopeTest {
- NSUInteger integerValue = 10;
- BlockWithNoParams myBlock = ^{
- NSLog(@"Integer value inside the block = %lu", (unsigned long) integerValue);
- };
- integerValue = 20;
- myBlock();
- NSLog(@"Integer value outside the block = %lu", (unsigned long) integerValue);
- }
输出结果为:
- Integer value inside the block = 10Integer value outside the block = 20
除非用存储类__block 前缀限定, 否则 Block Object 只会把方法作用域的局部变量传作为只读变量传递给 Block Object。
5、Block 的使用
如果你声明一个 block 作为变量,可以把它当成函数来使用。
你可以把一个 block 作为函数的参数就像其他任何参数那样。然而在很多情况下, 你不需要声明 blocks; 相反你只要简单在需要它们作为参数的地方内联实现它们。
Cocoa 提供了一系列使用 block 的方法。你可以把一个 block 作为方法的参数就像其他参数那样。
copy 相关
作为一种优化, block 存储在栈上面, 就像 blocks 本身一样。如果使用 Block_copy 拷贝了 block 的一个副本 (或者在 Objective-C 里面给 block 发送了一条 copy 消息), 变量会被拷贝到堆上面。所以一个__block 变量的地址可以随时间推移而被更改。
当你拷贝一个 block 时, 任何在该 block 里面对其他 blocks 的引用都会在需要的时候被拷贝, 即拷贝整个目录树 (从顶部开始)。如果你有 block 变量 A 并在该 block 变量 A 里面引用其他的 block 变量 B, 那么那个 block 变量 B 会被拷贝一份。
当你拷贝一个基于栈的 block 时, 你会获得一个新的 block 。但是如果你拷贝一个基于堆的 block , 你只是简单的递增了该 block 的引用数, 并把原始的 block 作为函数或方法的返回值。
在你希望 block 在它被声明的作用域被销毁后继续使用的话, 你只需要做一份拷贝。拷贝会把 block 移到堆里面。你可以使用 C 函数来 copy 和 release 一个 block:
Block_copy();
Block_release();
如果你使用 Objective-C, 你可以给一个 block 发送 copy、retain 和 release(或 autorelease) 消息。
为了避免内存泄露, 你必须总是平衡 Block_copy() 和 Block_release()。你必须平衡 copy 或 retain 和 release(或 autorelease) —— 除非是在垃圾回收的环境里面。
编写返回代码块的方法
有时我们会需要编写一个返回块代码的方法,如:
- + (ComputationBlock) raisedToPower: (int) y {
- ComputationBlock block = ^(int x) {
- return (int) pow(x, y);
- };
- return block; // Don't do this!}
但是当我们运行它时,会得到运行时错误 "EXC_BAD_ACCESS"。
解决这个问题的关键是了解代码块是怎么分配内存的。代码块的生命周期是在栈中开始的,因为在栈中分配内存是比较快的。是栈变量也就意味着它从栈中弹出后就会被销毁。方法返回结果就会发生这样的情况。解决办法是在返回之前将代码块从栈中移到堆中。
- + (ComputationBlock) raisedToPower: (int) y {
- ComputationBlock block = ^(int x) {
- return (int) pow(x, y);
- };
- return [[block copy] autorelease];
- }
6、使用 Block 简化回调
使用 Block 最大的便利就是简化的回调过程。以前使用 UIView 的动画, 进程要控制动画结束后进行相应的处理。iOS 4.0 之后, UIView 新增了对 Block 的支持, 现在只要使用简单的一个 Block 代码就可以在写动画的代码部分直接添加动画结束后的操作。还有就是在使用 Notification 时候 Block 也非常有帮助。
Block 代替 delegate 的简单实现:
http://www.cocoachina.com/bbs/job.php?action=download&aid=51512
具体可以看 github 的一个 demo,
https://github.com/zhangxigithub/BlockUI
7、Block 内存相关
在引用计数的环境里面,
1、默认情况下当你在 Block 里面引用一个 Objective-C 对象的时候,该对象会被 retain;
2、当你简单的引用了一个对象的实例变量时,它也会被 retain。
因为这样,你经常会添加__block 存储类型修饰符标识,这样做对象的变量才不会被 retain。
如果你在实现方法的时候使用了 block,则你必须遵循:
1、如果你通过引用来访问一个实例变量,self 会被 retain。
2、如果你通过值来访问一个实例变量,那么变量会被 retain。
最后,按照喵神的说法就是,如果你使用 ARC,那就不要管内存的情况了。
就爱阅读 www.92to.com 网友整理上传, 为您提供最全的知识大全, 期待您的分享,转载请注明出处。
来源: