iOS:Block 编程1--介绍与使用

参考:

官网<Blocks Programming Topics>,https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html

<Block编程值得注意的那些事儿>,http://www.cocoachina.com/macdev/cocoa/2013/0527/6285.html

--------------------------------------------------华丽分割线----------------------------------------------------------------

--前言:

         Block 对象是 C-level syntactic and runtime feature。它们和标准 C 函数很类似,但是除了 可执行代码外,它们还可能包含了variable bindings to automatic (stack) or managed (heap) memory.。A block can therefore maintain a set of state (data) that it can use to impact behavior when executed

         Block的使用很像函数指针,不过与函数最大的不同是:Block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,Block不仅实现函数的功能,还能携带函数的执行环境

         你可以用 blocks 来compose function expressions,这些表达式可以作为 API 使用,optionally stored,或被多个线程使用。Blocks 作为回调特别有用,because the block carries both the code to be executed on callback and the data needed during that execution

          available:iOS 4.0 and later.

注:闭包:

        闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境。

       可以这样理解,Block其实包含两个部分内容:

1.Block执行的代码,这是在编译的时候已经生成好的;
2.一个包含Block执行时需要的所有外部变量值的数据结构。 Block将使用到的、作用域附近到的变量的值建立一份快照拷贝到栈上。

        objective c 中的的闭包,是通过block实现的。

===================================================

========================Blocks概念和定义=============

------------------------Declaring a Block Reference-----------          

          Block variables hold references to blocks.你可以使用和声明函数指针类似的语法来声明它 们,除了它们使用 ^ 修饰符来替代 * 修饰符。Block 类型可以完全操作其他 C 系统类型。以下都是合法的 block 声明:

  void (^blockReturningVoidWithVoidArgument)(void);
  int (^blockReturningIntWithIntAndCharArguments)(int, char);
  void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
         Blocks 还支持 可变参数(...)。一个没有使用任何参数的 block 必须在参数列表 上面用 void 标明

         Blocks 被设计be fully type safe by giving the compiler a full set of metadata to use to validate use of blocks, parameters passed to blocks, and assignment of the return value。你可以把一个 block 引用强制转换为任意类型 的指针,反之亦然。但是你不能通过修饰符 * 来解引用(dereference)一个 block,因此一个 block 的大小是无法在编译的时候计算的。

        你同样可以创建 blocks 的类型。在多个地方定义相同的block类型。

typedef float (^MyBlockType)(float, float);
MyBlockType myFirstBlock = // ... ;

        block类型作为参数时候的申明:注意区别

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, BOOL *stop))block  // 或者(MyBlockType)block

------------------------创建一个Block-----------

        (block definition)使用 ^ 修饰符来标识一个 block 表达式的开始,它通常后面跟着一个被 () 包含起来的参数列表,Block 的主体一般被包含在 {} 里面。或者 create a block “inline”

int (^oneFrom)(int);
oneFrom = ^(int anInt) {
     return anInt - 1;
};
// 或者
int (^oneFrom)(int) = ^(int anInt) {
     return anInt - 1;
};
// 或者 create a block "inline"

----Global Blocks: At a file level, you can use a block as a global literal

#import <stdio.h>
int GlobalInt = 0;
int (^getGlobalInt)(void) = ^{ return GlobalInt; };
========================Conceptual Overview==========
          Block 对象为使用 C 语言和 C 派生语言(如 Objective-C 和 C++)提供了一个方式来创建特别(ad hoc)的函数作为表达式(hoc function body as an expression)。在其他语言和环境中,一个block对象有时候 被成为“ 闭包(closure)”。在这里,它们通常被口语化为” 块(blocks)”,除了在某 些范围它们容易和standard C term for a block of code混淆。

------Block功能

          一个 block 就是一个anonymous inline collection of code:
1.和函数一样拥有参数类型;
2.有inferred or declared return type

3.可以捕获它的声明所在相同作用域(lexical scope)的状态

4.Can optionally modify the state of the lexical scope
5.Can share the potential for modification with other blocks defined within the same lexical scope
6.Can continue to share and modify state defined within the lexical scope (the stack frame) after the lexical scope (the stack frame) has been destroyed

         你可以拷贝一个 block,甚至可以把它作为可执行路径(deferred execution)传递给其他线程(或者在自 己的线程内传递给run loop)。编译器和运行时会在整个block生命周期里面为所有 block 引用变量保留一个副本(The compiler and runtime arrange that all variables referenced from the block are preserved for the life of all copies of the block.)。尽管 blocks 在纯 C 和 C++上面可用,但是一个 block 也同样是一个 Objective-C 的对象。

------用处

         Blocks represent typically small, self-contained pieces of code. As such, they’re particularly useful as a means of encapsulating units of work that may be executed concurrently, or over items in a collection, or as a callback when another operation has finished.

         Blocks 作为传统回调函数的一个实用的替代办法,有以下两个原因:

1. 它们可以让你在调用的地方编写代码实现后面将要执行的操作。因此 Blocks 通常作为 framework methods的参数;

2.它们允许你访问局部变量。Rather than using callbacks requiring a data structure that embodies all the contextual information you need to perform an operation, you simply access local variables directly。

===============================================

=========================使用Blocks=============

1.调用一个block:

          declare a block as a variable,you can use it as you would a function

int (^oneFrom)(int) = ^(int anInt) {
    return anInt - 1;
};
printf("1 from 10 is %d", oneFrom(10));

float (^distanceTraveled) (float, float, float) =
               ^(float startingSpeed, float acceleration, float time) {
      float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
      return distance;
};
float howFar = distanceTraveled(0.0, 9.8, 1.0);
2.Block作为函数的参数:

         In many cases, you don’t need to declare blocks;usually create a block “inline”.

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    return strncmp(left, right, 1);
});
//在 dispatch_apply 函数中应用等
3.Block作为方法的参数:
NSArray *array = [NSArray arrayWithObjects: @"A", @"B", @"C", @"A", @"B", @"Z",@"G", @"are", @"Q", nil];
NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);
test = ^ (id obj, NSUInteger idx, BOOL *stop) {
    if (idx < 5) {
        if ([filterSet containsObject: obj]) {
             return YES;
        }
   }
   return NO;
};
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";
[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
       *stop = YES;
       found = YES;
    }
}];

------------备注:

1.非内联(inline) block 不能直接访问 self,只能通过将 self 当作参数传递到 block 中才能使用,并且此时的 self 只能通过 setter 或 getter 方法访问其属性,不能使用句点式方法。但内联 block 不受此限制(个人理解,在对象的方法中,改方法执行时,能引用到self,在对象的方法外的block根本没有self一说

typedef NSString* (^IntToStringConverter)(id self, NSInteger paramInteger); 
- (NSString *) convertIntToString:(NSInteger)paramInteger 
                 usingBlockObject:(IntToStringConverter)paramBlockObject 
{ 
    return paramBlockObject(self, paramInteger); 
}  
typedef NSString* (^IntToStringInlineConverter)(NSInteger paramInteger); 
- (NSString *) convertIntToStringInline:(NSInteger)paramInteger 
                 usingBlockObject:(IntToStringInlineConverter)paramBlockObject 
{ 
    return paramBlockObject(paramInteger); 
}  
IntToStringConverter independentBlockObject = ^(id self, NSInteger paramInteger) { 
    KSLog(@" >> self %@, memberVariable %d", self, [self memberVariable]);      
    NSString *result = [NSString stringWithFormat:@"%d", paramInteger]; 
    KSLog(@" >> independentBlockObject %@", result); 
    return result; 
};  
- (void)testAccessSelf 
{ 
    // Independent 
    [self convertIntToString:20 usingBlockObject:independentBlockObject];      
    // Inline 
    IntToStringInlineConverter inlineBlockObject = ^(NSInteger paramInteger) { 
        KSLog(@" >> self %@, memberVariable %d", self, self.memberVariable); 
         
        NSString *result = [NSString stringWithFormat:@"%d", paramInteger]; 
        KSLog(@" >> inlineBlockObject %@", result); 
        return result; 
    }; 
    [self convertIntToStringInline:20 usingBlockObject:inlineBlockObject]; 
} 

------------------------需要避免的模式------------------------

         A block literal (that is, ^{ ... }) is the address of a stack-local data structure that represents the block. The scope of the stack-local data structure is therefore the enclosing compound statement, so you should avoid the patterns shown in the following examples:

void dontDoThis() {
              void (^blockArray[3])(void);  // an array of 3 block references
              for (int i = 0; i < 3; ++i) {
                  blockArray[i] = ^{ printf("hello, %d\n", i); };
                  // WRONG: The block literal scope is the "for" loop
} }
void dontDoThisEither() {
              void (^block)(void);
              int i = random():
              if (i > 1000) {
                  block = ^{ printf("got i at: %d\n", i); };
                  // WRONG: The block literal scope is the "then" clause
              }
// ... }

 

------------------------Debugging------------------------    

你可以在 blocks 里面设置断点并单步调试。你可以在一个 GDB 的对话里面使用 invoke-block 来调用一个 block。如下面的例子所示:

$ invoke-block myBlock 10 20
        如果你想传递一个 C 字符串,你必须用引号括这它。例如,为了传递 this string 给 doSomethingWithString 的 block,你可以类似下面这样写
$ invoke-block doSomethingWithString "\"this string\""


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值