iOS7 Programming Cookbook-Chapter 7:Concurrency(Block部分)

来源:互联网 发布:sql 商业智能 编辑:程序博客网 时间:2024/05/22 09:00

笔者现在自学iOS开发中,想把部分学习的历程写到blog上分享,现在在看《iOS7 Programming Cookbook》,虽然都说CookBook没必要全看,但是还是想挑其中的一些章节重点学习一下,笔者语文老师死得早,写blog经验不多,如果有看的同学发现语句不通,表述不清还请见谅。

iOS7 Programming Cookbook第七章篇幅过长,所以按照内容相关度和篇幅切分成四部分来翻译,这是其中的第一部分Block部分。


7.1Constructing Block Objects(构建Block对象)

问题:在Objective-C中构建Block并且使用

解决方案:你需要理解block和经典的C函数之间的语法差别,区别将在下面的讨论部分详述。

讨论:

Block对象可以有内联和独立两种形式。我们从后一种开始学习。假设你有一个Objective-C方法,求两个NSInteger的差值,形式如下:

- (NSInteger) subtract:(NSInteger)paramValue from:(NSInteger)paramFrom{          return paramFrom - paramValue; }

现在,让我们把这段代码转化为具有相同功能的纯C函数。提供同样功能的纯C函数会使我们离学习block语法更进一步:

NSInteger subtract(NSInteger paramValue, NSInteger paramFrom){    return paramFrom - paramValue;}
你可以看到,C函数和Objective-C形式的语法有很大差别,我们再来看具有相同功能的block对象的代码:

NSInteger (^subtract)(NSInteger, NSInteger)= ^(NSInteger paramValue,NSInteger paramFrom){               return paramFrom - paramValue; };

在我们深入语法细节之前,先来看几个例子,假设我们有一个C函数,将一个NSUInteger类型变量作为参数,返回对应的NSString类型的字符串:

NSString* intToString (NSUInteger paramInteger){        return [NSString stringWithFormat:@"%lu", (unsigned long)paramInteger];            }
有同样功能的block对象代码如下:

NSString* (^intToString)(NSUInteger) = ^(NSUInteger paramInteger){    NSString *result = [NSString stringWithFormat:@"%lu",                        (unsigned long)paramInteger];    return result;};
最简单的独立block对象是没有任何参数,没有返回值的形式:

void (^simpleBlock)(void) = ^{    /* Implement the block object here */};
使用block对象的方法就和使用C函数是一样的,下面是一个例子:

NSString* (^intToString)(NSUInteger) = ^(NSUInteger paramInteger){    NSString *result = [NSString stringWithFormat:@"%lu",                        (unsigned long)paramInteger];    return result;};
- (void) callIntToString{    NSString *string = intToString(10);    NSLog(@"string = %@", string);}
CallIntToString这个Objective-C方法通过传递10这个值作为参数,并将返回值作为局部变量的形式调用intToString这个block对象。

现在我们已经知道了怎样以独立代码段的形式写Block了,让我们来看一看如何将block作为方法参数进行传值。我们需要稍微抽象一点理解下面这个例子。

假设我们有一个Objective-C方法。这个方法接收一个integer作为参数,在方法内进行一些转化,这个转化的进行依赖于程序内其他部分的运行情况。我们知道我们会将integer作为输入,string作为输出,但是我们要把转化的具体过程留给每次运行都会发生变化的block对象,让它来决定。因此,这个方法会同时接收奖杯转化的integer和执行转化的block作为参数。

我们使用之前的intToString作为block。现在我们需要一个将接收一个unsigned integer参数和一个block实体参数的Objective-C方法。为了方便接收intToString作为block参数,我们使用typedef告诉编译器接收什么样的block作为参数:

typedef NSString* (^IntToStringConverter)(NSUInteger paramInteger);
上面这段代码里,typedef告诉编译器将接收一个NSUInteger参数,并返回一个NSString作为结果的block对象表示为一个名为 IntToStringConverter的标识符。现在让我们继续编写这个接收integer和一个IntToStringConverter类型的block对象作为参数的Objective-C方法:
- (NSString *) convertIntToString:(NSUInteger)paramInteger usingBlockObject:(IntToStringConverter)paramBlockObject{    return paramBlockObject(paramInteger);}

我们现在来调用convertIntToString这个方法:

- (void) doTheConversion{    NSString *result = [self convertIntToString:123                               usingBlockObject:intToString];    NSLog(@"Result=%@",result);}
现在我们已经了解了独立block对象,接着让我们来看看内联block对象。在我们刚刚看过的doTheConversion方法,我们将intToString作为参数传入convertIntToString:UsingBlockObject:方法,那么如果我们还没有一个准备好的block实体作为参数呢?block对象是第一级函数,可以再运行时构建。让我们再来看看doTheConversion的另一种实现方法:
- (void) doTheConversion{        IntToStringConverter inlineConverter = ^(NSUInteger paramInteger){        NSString *result = [NSString stringWithFormat:@"%lu",                            (unsigned long)paramInteger];        return result;    };        NSString *result = [self convertIntToString:123                               usingBlockObject:inlineConverter];    NSLog(@"result = %@", result);}
除了构建内联block外,我们还可以在传值时构建block:

- (void) doTheConversion{        NSString *result =    [self convertIntToString:123          usingBlockObject:^NSString *(NSUInteger paramInteger) {                NSString *result = [NSString stringWithFormat:@"%lu",(unsigned long)paramInteger];                                    return result;    }];        NSLog(@"result = %@", result);    }


7.2 Accessing Variables in Block Objects(在Block中访问变量)

问题:你想要理解在方法和Block中访问变量的区别

解决方案:

下面是在Block对象里面使用变量时需要记住的要点:

  • block内的局部变量工作方式和方法中一模一样。
  • 对于内联Block来说,局部变量除了在Block中定义的变量外,还包括在方法中定义,但是在Block中实现的变量。
  • 你不能在独立Block中访问self,除非将self作为参数进行传值。
  • 只有当self在block创建的语法作用域中已经被定义了,那么就可以在内联block中访问self了。
  • 对于内联Block来说,在Block中实现的变量,可以同时执行读操作和写操作。
  • 对于内联Block来说,在方法中实现的变量,只能在Block内执行读操作。如果用__block来修饰的话,则也可以执行写操作。
  • 假设你有一个NSObject类型的对象,在这个对象的实现文件内,你要和GCD协同使用Block,那么在这个Block实现中,你对该NSObject的属性可以进行读写操作。
  • 在独立block中只能通过setter和getter来访问你的NSObject声明的属性,不能使用self.的形式。
讨论:
首先让我们来看看如何在block中使用局部变量的。一种是内联block,另一种是独立block:
void (^independentBlockObject)(void) = ^(void){    NSInteger localInteger = 10;    NSLog(@"local integer = %ld", (long)localInteger);    localInteger = 20;    NSLog(@"local integer = %ld", (long)localInteger);};
输出结果如下:
 local integer = 10 local integer = 20
然后我们再来看内联block:
- (void) simpleMethod{    NSUInteger outsideVariable = 10;    NSMutableArray *array = [[NSMutableArray alloc]                             initWithObjects:@"obj1",                             @"obj2", nil];    [array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {        NSUInteger insideVariable = 20;        NSLog(@"Outside variable = %lu", (unsigned long)outsideVariable);        NSLog(@"Inside variable = %lu", (unsigned long)insideVariable);        /* Return value for our block object */        return NSOrderedSame;    }];}
block实体可以对自己内部的变量进行读写操作,但是默认对于外部的变量只能进行读操作。为了允许block可以对外部变量执行写操作,我们必须在变量前加上__block存储类型的前缀:
- (void) simpleMethod{    __block NSUInteger outsideVariable = 10;    NSMutableArray *array = [[NSMutableArray alloc]                             initWithObjects:@"obj1",                             @"obj2", nil];    [array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {        NSUInteger insideVariable = 20;        outsideVariable = 30;        NSLog(@"Outside variable = %lu", (unsigned long)outsideVariable);        NSLog(@"Inside variable = %lu", (unsigned long)insideVariable);        /* Return value for our block object */        return NSOrderedSame;    }];}
只要self在内联block的词法作用域内被定义了,那么在内联block对象中就可以访问self。比如,下面这个例子中,block对象可以访问self,因为simpleMethod是一个Objective-C类的实例方法:
- (void) simpleMethod{    NSMutableArray *array = [[NSMutableArray alloc]                             initWithObjects:@"obj1",                             @"obj2", nil];    [array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {        NSLog(@"self = %@", self);        return NSOrderedSame;    }];}
你不能在独立Block中直接访问self,下面的访问就会产生编译错误:
void (^incorrectBlockObject)(void) = ^{NSLog(@"self = %@", self); /* self is undefined here */};
如果想要在独立Block中访问self,可以通过参数传递来访问,具体方法如下:
void (^correctBlockObject)(id) = ^(id self){    NSLog(@"self = %@", self);};- (void) callCorrectBlockObject{    correctBlockObject(self);}
让我们再来看看声明的属性,以及如何在block中访问他们。在内联Block中你可以使用self.的形式来对属性进行读写操作,例如:
#import "AppDelegate.h"@interface AppDelegate()@property (nonatomic, copy) NSString *stringProperty;@end@implementation AppDelegate
- (void) simpleMethod{    NSMutableArray *array = [[NSMutableArray alloc]                             initWithObjects:@"obj1",                             @"obj2", nil];    [array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {        NSLog(@"self = %@", self);        self.stringProperty = @"Block Objects";        NSLog(@"String property = %@", self.stringProperty);        /* Return value for our block object */        return NSOrderedSame;    }];}
但是在独立Block中,你无法使用self.的方式来对属性进行读写,在这个情境下,你应该使用setter和getter方法:
void (^correctBlockObject)(id) = ^(id self){ NSLog(@"self = %@", self);        /* This will work fine */        [self setStringProperty:@"Block Objects"];        /* This will work fine as well */        NSLog(@"self.stringProperty = %@",        [self stringProperty]);};
使用内联Block时,你需要记住一个重要的法则:内联block对象采用copy的方式复制他们语法作用域内的变量。如果你不理解这是什么意思,让我们来看一个例子

typedef void (^BlockWithNoParams)(void);- (void) scopeTest{    NSUInteger integerValue = 10;        BlockWithNoParams myBlock = ^{        NSLog(@"Integer value inside the block = %lu",              (unsigned long)integerValue);        integerValue = 20;        /* Call the block here after changing the         value of the integerValue variable */        myBlock();        NSLog(@"Integer value outside the block = %lu",              (unsigned long)integerValue);}
我们声明了一个integer局部变量,并复制为10。然后我们实现block对象,在这个block被实现之后,我们改变这个局部变量的值,然后再读取这个变量,并打印出来。你可能会期待block打印的变量值为20,但是你可以看到输出如下:
    Integer value inside the block = 10    Integer value outside the block = 20
在block中只是保持了在其实现的时候对于integerValue这个变量的只读复制。你可能会思考为什么block对象捕捉了一个局部变量的只读值。答案是这样简单。除非在局部变量前加上__block的前缀,语法作用域里的局部变量对于block内都是制度变量。因此,为了改变该行为,我们需要改变scopeTest的实现方法,通过加上__block前缀:
- (void) scopeTest{    __block NSUInteger integerValue = 10;    BlockWithNoParams myBlock = ^{        NSLog(@"Integer value inside the block = %lu", (unsigned long)integerValue);    };    integerValue = 20;    /* Call the block here after changing the     value of the integerValue variable */    myBlock();    NSLog(@"Integer value outside the block = %lu",          (unsigned long)integerValue);}
现在我们可以得到如下输出:
    Integer value inside the block = 20    Integer value outside the block = 20
这部分应该已经给出了足够的关于在block中使用变量的信息。建议可以写一些block,并尝试在其中使用变量,以更好地了解block中变量的使用。

7.3 Invoking Block Objects(使用Block对象)
问题:你已经了解了如何构建Block,现在你希望执行Block以得到结果
解决方法:与执行C函数的方法一模一样。
讨论:
如果你有一个独立block对象,你可以就像调用C方法一样调用它:
void (^simpleBlock)(NSString *) = ^(NSString *paramString){    /* Implement the block object here and use theparamString parameter */}
- (void) callSimpleBlock{    simpleBlock(@"O'Reilly");}
如果你想在另一个独立block对象中调用另一个独立block对象,指令和在block中调用一个C方法是一样的:
NSString *(^trimString)(NSString *) = ^(NSString *inputString){        NSString *result = [inputString stringByTrimmingCharactersInSet:                        [NSCharacterSet whitespaceCharacterSet]];    return result;};NSString *(^trimWithOtherBlock)(NSString *) = ^(NSString *inputString){    return trimString(inputString);};- (void) callTrimBlock{    NSString *trimmedString = trimWithOtherBlock(@" O'Reilly ");    NSLog(@"Trimmed string = %@", trimmedString);}
接着调用Objective-C方法callTrimBlock:
[self callTrimBlock];
0 0
原创粉丝点击