IOS ——OC——   代码块的理解

可以将一个代码块当做一个对象一样对待,并且可以递给
代码块本质上是和其他变量类似。不同的是,代码块存储的数据是一个函数体。使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值。

脱字符(^)是块的语法标记。按照我们熟悉的参数语法规约所定义的返回值以及块的主体(也就是可以执行的代码)。下图是如何把块变量赋值给一个变量的语法讲解:

按照调用函数的方式调用块对象变量就可以了:
int result = myBlock(4); // result是 28

1、参数是NSString*的代码块

 

[cpp] view plaincopy
 
  1. void (^printBlock)(NSString *x);  
  2. printBlock = ^(NSString* str)  
  3. {  
  4.     NSLog(@"print:%@", str);  
  5. };  
  6. printBlock(@"hello world!");  

运行结果是:print:hello world!

 

2、代码用在字符串数组排序

 

[cpp] view plaincopy
 
  1. NSArray *stringArray = [NSArray arrayWithObjects:@"abc 1", @"abc 21", @"abc 12",@"abc 13",@"abc 05",nil];  
  2. NSComparator sortBlock = ^(id string1, id string2)  
  3. {  
  4.     return [string1 compare:string2];  
  5. };  
  6. NSArray *sortArray = [stringArray sortedArrayUsingComparator:sortBlock];  
  7. NSLog(@"sortArray:%@", sortArray);  

运行结果:sortArray:(

 

    "abc 05",

    "abc 1",

    "abc 12",

    "abc 13",

    "abc 21"

)

3、代码块的递归调用

代码块想要递归调用,代码块变量必须是全局变量或者是静态变量,这样在程序启动的时候代码块变量就初始化了,可以递归调用

 

[cpp] view plaincopy
 
  1. static void (^ const blocks)(int) = ^(int i)  
  2. {  
  3.     if (i > 0) {  
  4.         NSLog(@"num:%d", i);  
  5.         blocks(i - 1);  
  6.     }  
  7. };  
  8. blocks(3);  

运行打印结果:

 

num:3

num:2

num:1

 4、在代码块中使用局部变量和全局变量

在代码块中可以使用和改变全局变量

 

[cpp] view plaincopy
 
  1. int global = 1000;  
  2. int main(int argc, const char * argv[])  
  3. {  
  4.     @autoreleasepool {  
  5.         void(^block)(void) = ^(void)  
  6.         {  
  7.             global++;  
  8.             NSLog(@"global:%d", global);  
  9.         };  
  10.         block();  
  11.         NSLog(@"global:%d", global);  
  12.     }  
  13.     return 0;  
  14. }  

 

运行打印结果:

global:1001

global:1001

而局部变量可以使用,但是不能改变。

 

 int local = 500;  
void(^block)(void) = ^(void)  
{  
      local++;  
    NSLog(@"local:%d", local);  
};  
block();  
NSLog(@"local:%d", local);  

在代码块中改变局部变量编译不通过。怎么在代码块中改变局部变量呢?在局部变量前面加上关键字:__block(前面是两个下划线)

例:

  1. __block int local = 500;  
  2. void(^block)(void) = ^(void)  
  3. {  
  4.     local++;  
  5.     NSLog(@"local:%d", local);  
  6. };  
  7. block();  
  8. NSLog(@"local:%d", local);  

运行结果:local:501

代码块

代码块对象(简称为代码块)是对C语言中函数的扩展。除了函数中的代码,代码块还包含变量绑定。代码块有时也被称为闭包(closure)。

代码块包含两种类型的绑定:自动型和托管型。自动绑定使用的是栈中的内存,而托管绑定使用的是通过堆创建的。

因为代码块实际上是由C语言实现的,所以他们在各种以C作为基础的语言内都是有效的,包括Objective-C、C++以及Objective-C++。

代码块在Xcode的GCC和Clang工具中是有效的,但它不属于ANSI的C语言标准。

代码块和函数指针

代码块借鉴了函数指针的用法。所以如果你知道如何声明函数指针,也就知道如何声明一个代码块。与函数指针类似,代码块具有以下特征:

  • 返回类型可以手动声明也可以由编译器推倒;
  • 具有指定类型的参数列表;
  • 拥有名称。

来看一下函数指针的声明。如下:

 

void (*my_func)(void);
这是简单的函数指针,它不接收参数和没有返回值。只要把*号替换成^,就可以把它转换为一个代码块的定义了。如下:

 

 

void (^my_func)(void);
在声明代码块变量和代码块实现的开头位置都要使用幂操作符。与函数中一样,代码块的代码要放在{ }(花括号)中。下面是一个代码块的实例:

 

 

int (^square_block)(int number) = 
^(int number) {return (number * number);};
int result = square_block(5);
printf(“Result = %d\n”, result);
这个代码块获取了一个整型的参数并返回这个数字的平方。等号前面是代码块的定义,而后面是实现内容。我们可以用如下关系来表示它们:

 

 

<returntype> (^blockname)() = ^{printf("Hello Blocks!\n");};
 

 

使用代码块

如果你将代码块声明成了一个变量,所以可以像函数一样使用它。如上面:int result = square_block(5); 这行代码并没有幂符号,因为只有在定义代码块的时候才需要使用它,调用时则不需要。

代码块有一个非常酷的特性可以替换原先的函数:代码块可以访问与它相同的(本地)有效范围内声明的变量,也就是说可以访问与它同时创建的有效变量。

 

int value = 6;
int (^multiply_block)(int number) = ^(int number)
{
        return (value * number);
}
int result = multiply_block(7);
printf(“Result = %d\n”, result);
 

 

直接使用代码块

使用代码块的时候通常不需要创建一个代码块变量,而是在代码中内联代码块的内容。通常,你会需要一个代码块作为参数的方法或函数。

 

NSArray *array = [NSArray arrayWithObjects: @“Amir”, @“mishal”, @“Irrum”, @“Adam”, nil];
NSLog(@“Unsorted Array %@”, array);
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(NSString *object1, NSString *object2) {
        return [object1 compare: object2];
}];
NSLog(@“Sorted Array %@”, sortedArray);
简单的创建了一个代码块,使用后就不需要它了。

 

使用typedef关键字

上面长的变量定义语句让人看的有点晕。我们可以用typedef关键字。

 

typedef double (^MKSampleMultiply2BlockRef)(double c, double d);
上面这行语句定义了一个名为MKSampleMultiply2BlockRef的代码块变量,它包含两个双浮点参数并返回一个双浮点数值。因此,我们可以像下面这样使用变量。

 

 

MKSampleMultiply2BlockRef multiply2 = ^(double c, double d)
{
        return c * d;
}
printf(“%f, %f”, mutiply2(4, 5), mutiply2(5, 2));
 

 

代码块和变量

代码块被声明后会捕捉创建点时的状态。代码块可以访问函数用到的标准类型的变量。

  • 全局变量,包括在封闭范围内声明的本地静态变量
  • 全局函数
  • 封闭范围内的参数
  • 函数级别(即,与代码块声明时相同级别)的_block变量。它们是可以修改的变量。
  • 封闭范围内的非静态变量会被获取为常量。
  • Objective-C的实例变量。
  • 代码块内部的本地变量。

本地变量

本地变量就是与代码块在同一范围内声明的变量。我们看下面的一个示例:

 

typedef double (^MKSampleMultiplyBlockRef)(void);
doub a = 10, b = 20;

MKSampleMultiplyBlockRef multiply = ^(void) {return a * b};
NSLog(@“%@”, multiply());

a = 20;
b = 50;

NSLog(@“%@”, multiply());
第二个NSLog语句会输出什么?1000?答案是200 。因为变量是本地的,代码块会在定义时复制并保存它们的状态。

 

全局变量

在上面的示例中,我们说过变量与代码块拥有相同的有效范围,可以根据需要把变量标记为静态的(全局的)。

 

 static doub a = 10, b = 20;

MKSampleMultiplyBlockRef multiply = ^(void) {return a * b};
NSLog(@“%@”, multiply());

a = 20;
b = 50;

NSLog(@“%@”, multiply());
 

 

参数变量

代码块中的参数变量具有和函数中的参数变量相同的作用。

 

typedef double (^MKSampleMultilply2BlockRef)(double c, double d);
MKSampleMultiply2BlockRef multiply2 = ^(double c, double d)
{
        return c * d;
}
NSLog(@“%f, %f”, mutiply2(4, 5), mutiply2(5, 2));

 

 

_block变量

本地变量会被代码块作为常量获取到。如果你想要修改它们的值,必须将它们声明为可修改的。否则,像下面这样,会出现出现错误。

 

double c = 3;
MKSampleMultiply2BlockRef multiply = ^(double a, double b){ c = a * b; };
编译器会给出警告。我们应该将变量c标记为_block。如下:

 

 

_block double c = 3;
MKSampleMultiply2BlockRef multiply = ^(double a, double b){ c = a * b; };
有些变量是不能声明为_block类型的。它们有两个限制:

 

  • 没有长度可变的数组;
  • 没有包含可变长度数组的结构体。

代码块内部的本地变量

 
这些变量和函数中的本地变量具有相同的作用。
 

//block作为函数的参数
#import <Foundation/Foundation.h>
//函数返回值类型  函数名(block的声明格式)
void fun(int (^block)(int a,int b))
{
   int sum1 = block(1,2);//代码块的调用
    NSLog(@"sum1=%d",sum1);
}

//NSString 类型的代码块作为fun1函数的形参
void fun1(NSString *(^myBlock)(NSString *s,NSString *s1)){
    NSLog(@"调用fun1后的结果:%@",myBlock(@"41651",@"46515"));//myBlock(@"41651",@"46515")相当于代码块的调用
}

void fun2(NSString *(^myBlock)(NSString *s),NSString *s1){
    NSLog(@"————%@",myBlock(s1));
}

int c = 0;//全局变量
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 声明格式:
        //返回值类型(^black名字)(形参列表)
        int (^myfun)();
        //block实现:
        //block 名字 = ^(形参列表){   }
        myfun = ^()
        {
            return 1;
        };
        //调用:
        //block名称(实参列表)有返回值的block,可以用一个变量进行接收;
        int a = myfun();
        NSLog(@"a=%d",a);
        //有返回值,有形参,声明和实现放在一起;
        int (^myblock)(int a,int b)=^(int a,int b)
        {
            return 1+b;
       
        };
        //调用
        int sum = myblock(10,20);
        NSLog(@"sum=%d",sum);
       
        //返回值类型是NSString的代码块
        NSString *(^myblock1)(NSString *s) = ^(NSString *s)
        {
            NSLog(@"字符串s为:%@",s);
            return s;
       
        };
        myblock1(@"4865");
       
        //有一个局部变量,要在block中进行值的改变
        __block int b = 0;//声明代码块局部变量的方式;
        void (^myblock2)() = ^()
        {
            b++;
        };
           myblock2();
        NSLog(@"b=%d",b);
       
        //有一个全局变量,在block进行值的改变
       
        void (^myBlock3)() = ^(){
            c++;
        };
        myBlock3();
        NSLog(@"c=%d",c);
       
        //block最为函数的形参的调用
        //当一个block作为函数的参数时,其返回值类型,形参个数及类型要与函数形参格式保持一致;
       
        int (^myblock4)(int c,int d)= ^(int a,int b)
        {
            return  a+b;
        };
        //(第一次调用fun)函数的形参是block类型,调用时,直接传block的名字。
        fun(myblock4);//fun函数调用;
       
       
        //(第二次调用fun)内联block格式:^返回值类型(形参列表){}
        fun(^int(int a,int b)
        {
            return a*b;
        });//可以用这种方法代替上面第一种调用的方法;
       
       //使用内联block调用fun1函数
      fun1(^NSString *(NSString *s, NSString *s1) {
           NSString *str = [s stringByAppendingString:s1];//代码块的作用实现两个字符串的拼接;
          return str;//代码块的返回值
      });//()里面的内容相当于fun1的实参{}里面的内容相当于代码块的功能;
       
       
        fun2(^NSString *(NSString *s) {
            NSRange range = NSMakeRange(0, 3);
            NSString *str1 = [s substringWithRange:range];
            return str1;
        }, @"4326542");
    }
    return 0;
}
运行结果:
a=1
sum=21
字符串s为:4865
b=1
c=1
sum1=3
sum1=2
调用fun1后的结果:4165146515
---432
 
//block作为方法的参数
 
类接口:
#import <Foundation/Foundation.h>
//使用typedef声明block: typedef 返回值类型 (^名字)(形参列表)
typedef int (^myblockType)(int a);

@interface Person : NSObject

@property(nonatomic ,strong)NSString *name;
//block作为方法的参数:
//(返回值及类型(^)(形参列表))参数名(block名字)
//其中形参列表中可以  只有形参类型名没有形参变量
-(void)myblock:(int (^)(int a))block;


-(void)sengstr:(NSString *)name  andblock:(NSString *(^)(NSString *))myblock;


-(void)useblockType:(myblockType)sss;
@end
类实现:
#import "Person.h"

@implementation Person
-(void)myblock:(int (^)(int))block
{
    int b = block(30);//代码块的实现
    NSLog(@"b=%d",b);

}
-(void)sengstr:(NSString *)name andblock:(NSString *(^)(NSString *))myblock//name是一个参数myblock是一个参数
{
    myblock(name);
}

-(void)useblockType:(myblockType)sss//将代码(myblockType)块看做是int
{
    int f = sss(2);
     NSLog(@“f=%d",f);
}
@end
主程序:
#import <Foundation/Foundation.h>
#import"Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
        Person *p = [[Person alloc]init];
        int (^youblock)(int d) = ^(int g)
        {
            return g*7;
        };//定义一个代码块形式的参数
        [p myblock:youblock];//将代码块作为参数传给myblock方法实现myblock方法的调用;
               
        [p myblock:^int(int a) {
            return a+6;
        }]; //使用内联block直接调用myblock方法
       
    [p sengstr:@"王老五" andblock:^NSString *(NSString *s) {
        NSLog(@"我叫:%@",s);
        return s;
    }];
   
        [p useblockType:^int(int c) {
            return c*5;
        }];
   
    }
    return 0;
}
运行结果:
b=210
b=36
我叫:王老五
f=10
posted @ 2015-04-17 12:12  #零下一度&  阅读(515)  评论(0编辑  收藏  举报