zoukankan      html  css  js  c++  java
  • 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
    ”, result);
    
    这个代码块获取了一个整型的参数并返回这个数字的平方。等号前面是代码块的定义,而后面是实现内容。我们可以用如下关系来表示它们:

     

     

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

     

    使用代码块

    如果你将代码块声明成了一个变量,所以可以像函数一样使用它。如上面: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
    ”, 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
    感谢您的访问! 若对您有帮助或有兴趣请关注博客:http://www.cnblogs.com/Rong-Shengcom/
  • 相关阅读:
    OCP-1Z0-053-V12.02-541题
    OCP-1Z0-053-V12.02-544题
    OCP-1Z0-053-V12.02-545题
    OCP-1Z0-053-V13.02-711题
    OCP-1Z0-053-V12.02-493题
    OCP-1Z0-053-V13.02-696题
    OCP-1Z0-053-V12.02-522题
    OCP-1Z0-053-V12.02-523题
    OCP-1Z0-053-V12.02-534题
    OCP-1Z0-053-V13.02-692题
  • 原文地址:https://www.cnblogs.com/Rong-Shengcom/p/4983936.html
Copyright © 2011-2022 走看看