iOS总结 - Block

1Block简介

   Block,可以当做是Objective-C的匿名函数,在两个对象之间将任意的语句当做数据进行传递,比引用定义在别处的函数直观。简单说,Block就是可以作为变量的函数方法。

2Block的创建

    Block的使用与函数有相同的机制,可以像声明函数一样,来声明一个bock变量;像定义一个函数,一样定义一个block;将block当做一个函数来调用。

void(^block)(void) = ^{
      NSLog(@"I'm a block!");
};

上面的代码中创建了一个名为block的变量,并把一个简单的block代码赋值给这个变量。

  void(^block)(int a) = ^{
      NSLog(@"I'm a block! a = %i", a);
  };


上面的代码,给block传递了一个变量:

  int(^block)(void) = ^{
      NSLog(@"I'm a block!");
      return 1;
  };

上面的代码,给block中返回一个值:

    double (^areaOfRectangle)(double length,double width) = ^ {
        return length / width;
    };
    NSLog(@"area with 4 and 5", areaOfRectangle(4,5));

      在上面的代码中,利用插入符(^)area变量标记为一个block。就像声明函数一样,需要包含返回值的类型,以及参数的类型,这样编译器才能安全的进行强制类型转换。插入符(^)跟指针(例如 int *aPointer)前面的星号(*)类似——只是在声明的时候需要使用,之后用法跟普通的变量一样。


3Block的闭包性(closure)

    block内部,可以像普通函数一样访问数据:局部变量、传递给block的参数,全局变量/函数。并且由于block具有闭包性,所以还能访问非局部变量(non-local variable)。非局部变量定义在block之外,但是在block内部有它的作用域。

  NSString *make = @"Honda";
  NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
      return [make stringByAppendingFormat:@" %@", model];
  };
  NSLog(@"%@", getFullCarName(@"Accord"));    // Honda Accord

    上述代码中,getFullCarName可以使用定义在block前面的make变量:

非局部变量会以const变量被拷贝并存储到block中,也就是说block对其是只读的。

如果尝试在block内部给make变量赋值,会抛出编译器错误。

    以const拷贝的方式访问非局部变量,意味着block实际上并不是真正的访问了非局部变量——只不过在block中创建了非局部变量的一个快照。当定义block时,无论非局部变量的值是什么,都将被冻结,并且block会一直使用这个值,即使在之后的代码中修改了非局部变量的值。

 NSString *make = @"Honda";
  NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
      return [make stringByAppendingFormat:@" %@", model];
  };
  NSLog(@"%@", getFullCarName(@"Accord"));    // Honda Accord
  // Try changing the non-local variable (it won't change the block)
  make = @"Porsche";
  NSLog(@"%@", getFullCarName(@"911 Turbo")); // Honda 911 Turbo


     block的闭包性为block与上下文交互的时候带来极大的便利性,当block需要额外的数据时,可以避免使用参数——只需要简单的使用非局部变量即可。


4、修改非局部变量

       冻结中的非局部变量是一个常量值,是一种默认的安全行为,从而防止在block中的代码对非局部变量做了意外的修改。

      如果在block中对非局部变量值进行修改,用__block存储修饰符(storage modifier)来声明非局部变量:

通过引用的方式访问非局部变量和普通函数中的静态局部变量(static local variable)类似,用__block修饰符声明的变量可以记录着block多次调用的结果。


 __block int i = 0;
  int (^count)(void) = ^ {
      i += 1;
      return i;
  };
  NSLog(@"%d", count());    // 1
  NSLog(@"%d", count());    // 2
  NSLog(@"%d", count());    // 3


5Block作为函数的参数

      把block存储在变量中有时候非常有用,比如将其用作函数的参数。这可以解决类似函数指针能解决的问题,不过我们也可以定义内联的block,这样代码更加易读。

      例如下面Car interface中声明了一个方法,该方法用来计算汽车的里程数。这里并没有强制要求调用者给该方法传递一个常量速度,相反可以改方法接收一个block——block根据具体的时间来定义汽车的速度。

// Car.h
  #import 

  @interface Car : NSObject
  @property double odometer;
  - (void)driveForDuration:(double)duration
         withVariableSpeed:(double (^)(double time))speedFunction
                    steps:(int)numSteps;
 @end

     代码中block的数据类型是double (^)(double time),也就是说block的调用者需要传递一个double类型的参数,并且该block的返回值为double类型。

注意:上面代码中的语法基本与本文开头介绍的block变量声明相同,只不过没有变量名字。

在函数的实现里面可以通过speedFunction来调用block

  // Car.m
  #import "Car.h"
  @implementation Car
  @synthesize odometer = _odometer;
  - (void)driveForDuration:(double)duration
         withVariableSpeed:(double (^)(double time))speedFunction
                    steps:(int)numSteps {
    double dt = duration / numSteps;
     for (int i=1; i<=numSteps; i++) {
         _odometer += speedFunction(i*dt) * dt;
     }
 }
 @end

     在下面的代码中,有一个main函数,在main函数中block定义在另一个函数的调用过程中。虽然理解其中的语法需要话几秒钟时间,不过这比起另外声明一个函数,再定义withVariableSpeed参数要更加直观。


 Car *theCar = [[Car alloc] init];
    // Drive for awhile with constant speed of 5.0 m/s
    [theCar driveForDuration:10.0
                withVariableSpeed:^(double time) {
                            return 5.0;
                        } steps:100];
         NSLog(@"The car has now driven %.2f meters", theCar.odometer);

         // Start accelerating at a rate of 1.0 m/s^2
         [theCar driveForDuration:10.0
                withVariableSpeed:^(double time) {
                            return time + 5.0;
                        } steps:100];
         NSLog(@"The car has now driven %.2f meters", theCar.odometer);
     }


上面利用一个简单的示例演示了block的通用性。在iOSSDK中有许多API都利用了block的其它一些功能。

总结

        Block不仅提供了C函数同样的功能,而且block看起来更加直观。block可以定义为内联(inline),这样在函数内部调用的时候就非常方便,由于block具有闭包性(closure),所以block可以很容易获得上下文信息,而又不会对这些数据产生负面影响。

Swift 中的闭包

    闭包,是一些小的匿名代码块,可以像函数一样使用,将闭包传递给其他函数。闭包可以作为表达式,函数参数,函数返回值,闭包的运算结果是一种函数类型。同Object-c 中的 Block

func calculate(opr :String)-> (Int,Int)-> Int {
    var result : (Int,Int)-> Int
    switch (opr) {
    case "+" :
        result = {(a:Int, b:Int) -> Int in
            return a + b
        }
    default:
        result = {(a:Int, b:Int) -> Int in
            return a - b
        }
    }
    return result;
}

let f1:(Int,Int)-> Int = calculate("+")
println("10 + 5 = \(f1(10,5))")

let f2:(Int,Int)-> Int = calculate("-")
println("10 + 5 = \(f2(10,5))”)

  闭包的表达式

  {(a:Int, b:Int) -> Int in
            return a + b
        }
result = {a, b in  return a + b }//简化:
result = {a, b in  a + b }  //隐藏:闭包内只有一条语句
 result = { $0 + $1 }  //缩写,$n+1 代表第n个参数

  使用闭包返回值

let c1:Int = {(a:Int, b:Int) -> Int in
                return a + b
             }(10,5)
println("10 + 5 = \(c1)")

  使用尾随闭包

func calculate(opr:String, funN:(Int,Int)-> Int) {
    switch (opr) {
    case "+" :
        println("10 + 5 = \(funN(10,5))")
    default:
        println("10 - 5 = \(funN(10,5))")
    }
}

calculate("+", {(a:Int, b:Int) -> Int in return a + b })
calculate("-"){(a:Int, b:Int) -> Int in return a + b }

捕捉上下文常量和变量

func makeArray() -> (String)->[String] {
    
    var ary:[String] = [String]()
    
    func addElement(element:String) -> [String] {
        ary += element
        return ary
    }
    
    return addElement;
}


let f1 = makeArray()
println("---f1---")
println(f1("张三"))
println(f1("李四"))
println(f1("王五"))

println("---f2---")
let f2 = makeArray();


你可能感兴趣的:(ios)