Blocks 简明实用指南(A Short Practical Guide to Blocks)

BlockC语言一个非常强大的特性,是Cocoa应用开发的一部分。他们类似RubyPythonLisp等脚本和编程语言中的“闭包”和“lambdas”。虽然乍一看觉得block的语法和存储很神秘,其实你会发现在项目中使用block很容易。

下面将讨论block的高级特性并举例说明block典型使用方法。Block明确描述请参考block编程主题。

目录:

为什么使用block

block系统框架API

Block和并发性

为什么使用block

Block是对象封装单元,或者从抽象的角度来看,是一段可以在任何时间执行的一段代码。从本质上来讲,block是便携的匿名函数,可以作为方法和函数传递的参数或返回值。Block本身有一个类型参数列表,可以推断或声明返回类型。也可以将block分配给一个变量,然后像调用函数一样调用block

Block的语法标记是插入符号(^) 。例如,下面代码声明了一个block变量,有两个integer参数并返回一个integer值。在第二个插入符号后是参数列表,在大括号中是实现代码,并将这些分配给Multiply 变量。

int (^Multiply)(int, int) = ^(int num1, int num2) {
    return num1 * num2;
};
int result = Multiply(7, 4); // Result is 28.

作为方法或函数的参数,block是一种回调,可以视为仅限于方法或函数的委托。通过block调用代码,可以定制一个方法或函数的行为。当block被调用,在适当的时间,方法或函数执行代码并通过block返回到调用代码,用于请求更多信息或获取特定应用程序行为。

Block作为方法或函数的参数有个优势是:在调用点提供回调代码。因为这段代码不需要在一个单独的方法或函数里实现,实现代码可以更简单和更容易理解。以通知的NSNotification为例,在“传统”的方法中,一个对象将本身作为通知的观察者,然后实现一个单独的方法(在 addObserver:.. 方法中标识为selector)来处理程序通知。

- (void)viewDidLoad {
   [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(keyboardWillShow:)
        name:UIKeyboardWillShowNotification object:nil];
}
 
- (void)keyboardWillShow:(NSNotification *)notification {
    // Notification-handling code goes here.
}

使用addObserverForName:object:queue:usingBlock: 方法,可以以通知处理程序代码巩固调用方法。

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification
         object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
             // Notification-handling code goes here. 
    }];
}

相对于其他形式的回调,block具有更有价值的优势,block可以在局部语法范围内共享数据。如果实现的方法中定义一个block,这个block可以访问方法的局部变量和参数(包括堆栈变量),也可以访问函数和全局变量包括实例变量。这个访问默认是只读,但是声明一个__block修饰符变量,可以在block中修改该变量的值。即使在包括block的方法或函数已经返回并且局部作用域被销毁,只要有block的引用,局部变量作为block对象的一部分也会一直存在。

Block系统框架API

使用block显而易见的动机是,越来越多的系统框架采用block作为参数的方法和函数。在框架方法中可以找到很多block的用例。

  1. 完成处理程序
  2. 通知处理程序
  3. 错误处理程序
  4. 枚举
  5. 视图动画和过渡
  6. 排序

接下来的章节描述了各种情况。在那之前,这里有一个关于在框架方法中声明block的概述。考虑以下NSSet类的方法:

- (NSSet *)objectsPassingTest:(BOOL (^)(id obj, BOOL *stop))predicate

Block声明表明,方法传递一个动态类型对象和一个引用boolean值到block(每个枚举项),block返回一个boolean值。(这些参数和返回值实际上包含在枚举中。)当指定block,以插入符号开始,加上括号的参数列表,以及由大括号封闭的block代码本身。

[mySet objectsPassingTest:^(id obj, BOOL *stop) {
    // Code goes here: Return YES if obj passes the test and NO if obj does not pass the test.
}];

完成和错误处理程序

完成处理程序是一种回调机制,当框架方法或函数完成时,允许客户端完成某些行为。通常客户端使用完成处理程序来释放状态或更新用户界面。一些框架方法用block来实现完成处理程序(或者说代理方法或通知处理程序)。

UIView类有多个动画和视图切换的类方法,这些类方法使用完成处理程序block作为参数。(视图动画和过渡中有这些方法的概述。)在列表1-1中的示例中显示了 animateWithDuration:animations:completion:方法的实现。本例中的完成处理程序在动画结束几秒后重置动画视图到原始位置和透明度值(alpha)。

完成处理程序

- (IBAction)animateView:(id)sender {
    CGRect cacheFrame = self.imageView.frame;
    [UIView animateWithDuration:1.5 animations:^{
        CGRect newFrame = self.imageView.frame;
        newFrame.origin.y = newFrame.origin.y + 150.0;
        self.imageView.frame = newFrame;
        self.imageView.alpha = 0.2;
    }
                     completion:^ (BOOL finished) {
                         if (finished) {
                             // Revert image view to original.
                             self.imageView.frame = cacheFrame;
                             self.imageView.alpha = 1.0;
                         }
    }];
}

一些框架方法有错误处理程序,该程序是类似完成处理程序的block参数。当因为一些错误条件而不能完成任务时,该方法调用他们(并传递一个NSError对象)。通常通过错误处理程序来通知用户错误信息。

通知处理程序

当设置观察者时,NSNotificationCenter对象的addObserverForName:object:queue:usingBlock: 方法实现通知处理程序。清单1-2说明了如何调用这个方法,即为通知定义一个block处理程序。正如通知处理方法一样,传入一个NSNotification对象。该方法也创建一个NSOperationQueue实例,这样应用程序可以在指定上下文处运行block处理程序。

添加一个对象作为观察者,并使用block处理通知

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    opQ = [[NSOperationQueue alloc] init];
    [[NSNotificationCenter defaultCenter] addObserverForName:@"CustomOperationCompleted"
             object:nil queue:opQ
        usingBlock:^(NSNotification *notif) {
        NSNumber *theNum = [notif.userInfo objectForKey:@"NumberOfItemsProcessed"];
        NSLog(@"Number of items processed: %i", [theNum intValue]);
    }];
}

枚举

基础框架的集合类——NSArrayNSDictionaryNSSetNSIndexSet,声明的方法执行集合特定类型的枚举值和为客户端指定block提供代码处理或测试每个枚举项。换句话说,该方法执行fast-enumeration构造方法。

for (id item in collection) {
    // Code to operate on each item in turn.
}

一般有两种带block的枚举方法。第一种方法是命名以枚举开头并且没有返回值。这些方法的block在每个枚举项上执行一些工作。第二种方法是以passingTest开头,这种方法返回一个整数或一个NSIndexSet对象。这些方法的block针对每个枚举项进行测试,如果通过测试则返回YEStrue。用整数或索引标识通过测试的源集合中的对象或对象集。

清单1-3中的代码针对每种类型调用一个NSArray方法。第一个方法的block(“通过测试”方法)如果数组中每个string有某一前缀则返回YEStrue。随后的代码创建一个临时数组使用方法返回的索引。第二个方法的block除去每个string的前缀,并将其添加到一个新的数组。

清单1-3中的代码调用每种类型的NSArray方法。

使用两个block处理枚举数组

NSString *area = @"Europe";
NSArray *timeZoneNames = [NSTimeZone knownTimeZoneNames];
NSMutableArray *areaArray = [NSMutableArray arrayWithCapacity:1];
NSIndexSet *areaIndexes = [timeZoneNames indexesOfObjectsWithOptions:NSEnumerationConcurrent
                                passingTest:^(id obj, NSUInteger idx, BOOL *stop) {
    NSString  *tmpStr = (NSString *)obj;
    return [tmpStr hasPrefix:area];
}];
 
NSArray *tmpArray = [timeZoneNames objectsAtIndexes:areaIndexes];
[tmpArray enumerateObjectsWithOptions:NSEnumerationConcurrent|NSEnumerationReverse
                           usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                               [areaArray addObject:[obj substringFromIndex:[area length]+1]];
}];
NSLog(@"Cities in %@ time zone:%@", area, areaArray);

在这些枚举方法中的停止参数(这个例子中没有使用)允许block传递YEStrue回方法,告知方法退出枚举。当仅仅想在集合中寻找符合条件的第一项。

NSString类虽然不代表一个集合,但有两个带block参数的方法,方法名字以enumerate: enumerateSubstringsInRange:options:usingBlock:enumerateLinesUsingBlock:开头。第一种方法列举了一个指定粒度的文本单元中的字符串(行,段落,单词,句子等等)。第二种方法仅列举了行。清单1-4演示了如何使用第一种方法。

清单1-4使用block查找匹配的子字符串

NSString *musician = @"Beatles";
NSString *musicDates = [NSString stringWithContentsOfFile:
    @"/usr/share/calendar/calendar.music"
    encoding:NSASCIIStringEncoding error:NULL];
[musicDates enumerateSubstringsInRange:NSMakeRange(0, [musicDates length]-1)
    options:NSStringEnumerationByLines
    usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
           NSRange found = [substring rangeOfString:musician];
           if (found.location != NSNotFound) {
                NSLog(@"%@", substring);
           }
      }];

视图动画和过渡

iOS4.0中的UIView类介绍了几个带block的类方法,这些方法用于动画和视图过渡。Block参数有两种(不是所有方法都带这两种参数):

Block改变视图属性呈现动画

完成处理程序

清单1-5中显示了animateWithDuration:animations:completion:的调用。改方法有两种block参数。在这个例子中,在动画中(通过指定alpha0)使视图消失,在完成处理程序中将它从子视图中删除。

使用block实现的简单动画

[UIView animateWithDuration:0.2 animations:^{
        view.alpha = 0.0;
    } completion:^(BOOL finished){
        [view removeFromSuperview];
    }];

其他UIView类方法在两个视图过渡中执行,包括翻转和旋转。在清单1-6例子中调用transitionWithView:duration:options:animations:completion: ,用向左翻转动画来实现子视图的替换。(并没有实现完成处理程序。)

实现两个视图过渡的翻转效果

[UIView transitionWithView:containerView duration:0.2
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{
                    [fromView removeFromSuperview];
                    [containerView addSubview:toView]
                }
                completion:NULL];

排序

基础框架声明了NSComparator类型用于比较两个项目。

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);

NSComparator是有两个对象并返回NSComparisonResult值的block类型。它是NSSortDescriptor, NSArrayNSDictionary方法的一个参数,并被这些类的实例用于排序。清单1-7给出了使用例子。

使用NSComparator block对数组进行排序

NSArray *stringsArray = [NSArray arrayWithObjects:
                                 @"string 1",
                                 @"String 21",
                                 @"string 12",
                                 @"String 11",
                                 @"String 02", nil];
static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
        NSWidthInsensitiveSearch | NSForcedOrderingSearch;
NSLocale *currentLocale = [NSLocale currentLocale];
NSComparator finderSort = ^(id string1, id string2) {
    NSRange string1Range = NSMakeRange(0, [string1 length]);
    return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
};
NSLog(@"finderSort: %@", [stringsArray sortedArrayUsingComparator:finderSort]);

这个例子来自block编程主题。

Block和并发性

Block是封装在可异步执行的工作单元中的匿名可移植对象。因为这个基本事实,blockGCDNSOperationQueue类的中心要素,推荐这两种技术进行并发处理。

GCD的两个核心功能,dispatch_sync(3) OS X 开发工具手册页面(同步调用)或dispatch_async(3) OS X 开发工具手册页面(异步调用)作为block的第二参数。

NSOperationQueue是一个对象,用于安排任务并发执行或按照依赖关系定义的顺序执行。NSOperation对象经常使用block来执行任务。

更多关于CCD,可参阅NSOperationQueueNSOperation



官方原文:

https://developer.apple.com/library/ios/featuredarticles/Short_Practical_Guide_Blocks/ 


1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值