zoukankan      html  css  js  c++  java
  • [转]blocks编程

    原文地址:http://geeklu.com/2012/01/block/

    • 介绍
    • 声明创建和调用
    • Block和变量
    • Block实际应用

    1.介绍

    Block是一个C Level的语法以及运行时的一个特性,非常像标准C中的函数(函数指针),但是其运行需要编译器和运行时支持,目前LLVM+Clang可以很好的支持Block(苹果修改过的GCC也可以)。Block和函数不同的是其语义 闭包 特性,以及可以有匿名block的存在。 你可以在LLVM的官方网站查看Block语言规范.
    你可以通过^ 运算符来声明一个block变量,或用来表明block定义的开始,而block的代码块则是包含在一对花括号{}内的.

    int multiplier = 2;   
    int (^myBlock)(int) = ^(int num){   
        return num * multiplier;   
    };   
    printf("%d",myBlock(4));   
    

    上面代码中的myBlock就是Block的变量名,由myBlock变量的声明可以看出,它返回值为int类型,且存在一个int型的参数。 等于号后面就是Block的定义并将其赋值给myBlock . Block的调用就和C函数的使用类似.

    2. 声明创建和调用

    声明Block变量
    Block变量保存着指向Block的指针,声明一个Block变量就和声明一个函数指针变量类似,只是将*改成了^. 其他的就和C的类型系统保持一致了。

    void (^blockReturningVoidWithVoidArgument)(void);   
    int (^blockReturningIntWithIntAndCharArguments)(int, char);   
    void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
    

    另 Block还支持可变参数variadic (...) ,没有参数的话,变量列表的地方必须写上void关键字.

    声明Block类型 你可以通过typedef声明Block的类型,这样多个地方需要使用同种类型的Block的时候会比较方便,

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

    Block创建 声明了一个Block变量之后,可以为这个变量赋值

    blockReturningVoidWithVoidArgument = ^{   
          printf("%s","Block Returing Void With Void Argument.");   
    }
    

    当然你可以在声明变量的同时赋值

    void (^blockReturningVoidWithVoidArgument)(void) = ^{   
          printf("%s","Block Returing Void With Void Argument.");   
    }
    

    Block调用 Block的调用和函数的调用是非常相似的,如上面定义的blockReturningVoidWithVoidArgument,调用的时候则直接 blockReturningVoidWithVoidArgument();便可.

    匿名Block 当一个Block作为函数参数的时候,一般实参都是以匿名Block的方式传过去的。

    void callVoidVoid(void (^closure)(void)) {   
       closure();   
    }   
    int main(int argc, char *argv[]) {   
        __block int i = 10;       
        callVoidVoid(^{ ++i; });   
       if (i != 11) {   
           printf("*** %s didn't update i
    ", argv[0]);   
           return 1;   
       }   
       printf("%s: success
    ", argv[0]);   
       return 0;   
    }
    

    当然你也可以直接调用匿名Block,如

    ^{ ++i; }();
    

    3.Block和变量

    一个Block的内部是可以引用自身作用域外的变量的,包括static变量,extern变量或自由变量(定义一个变量的时候,如果不加存储修饰 符,默认情况下就是自由变量auto,auto变量保存在stack中的.除了auto之外还存在register,static等存储修饰符) ,对于自由变量,在Block中是只读的。在引入block的同时,还引入了一种特殊的变量存储修饰符__block,通过它 的变量叫做block变量,block变量在block内部可以进行写操作的。这些变量中,自由变量是最特殊的,在Block声明的时候,自由变量在 Block内部只读且其值被固定住(自由变量被拷贝了一份,且限定为const),即使在block调用前改变了这个自由变量的值,block调用的时 候,看到的却还是block声明的时候的那个值。

    代码示例 3.1

    __block int blockValue = 0;
    int autoValue = 0;
    
    void(^printValue)(void) = ^{
        printf("blockValue = %d
    ",blockValue);
        printf("autoValue = %d
    ",autoValue);
    
    };
    blockValue ++;
    autoValue ++;
    printValue();
    

    3.1中的代码,输出的值为

    blockValue = 1
    autoValue = 0
    

    可以看到自由变量尽管自增了,但是在调用printValue这个Block的时候,看到的还是其定义的时候看到的那个autoValue的值,autoValue的值在Block的内部示无法修改的,要不然编译器会报错:

    Semantic Issue: Variable is not assignable (missing __block type specifier)
    

    Block定义时内存是分配在stack上的,当其作用域结束,就会被自动释放,所以你不需要去担心它的内存情况,我们可以对一个Block进行Block_copy()操作,Block_copy()之后,Block会被拷贝到heap中的内存中,且其所有的引用到的自由变量也将会被拷贝,当然你得记得通过Block_release()释放heap的内存空间哦。 在objc中Block是和对象一样被看作一等公民的(其实这是objc的Block扩展的功劳),你可以像使用对象那样对Block进行retain(retain只对heap中的Block起作用),copy以及release操作.

    在Block内部如果引用到对象或者对象的成员变量,那么当Block被拷贝Block_copy()之后,这个对象的引用计数会增加。

    代码示例 3.2

    NSObject *testObject = [[NSObject alloc] init];
    
    NSLog(@"%lu",[testObject retainCount]); //1
    NSLog(@"%lu",[self retainCount]); //1
    
    void(^testBlock)(void) = ^{
           NSLog(@"The Test String : %@", testObject);
       NSLog(@"The Window Object : %@", _window);
    };
    
    NSLog(@"%lu",[testObject retainCount]); //1
    NSLog(@"%lu",[self retainCount]); //1
      
    void(^testBlock2)(void) = Block_copy(testBlock); //testBlock会被拷贝到heap中,所以用完了要自己调用Block_release进行释放
    NSLog(@"%lu",[testObject retainCount]); //2
    NSLog(@"%lu",[self retainCount]); //2
    
    testBlock2();
    
    Block_release(testBlock2);
    
    NSLog(@"%lu",[testObject retainCount]); //1
    NSLog(@"%lu",[self retainCount]); //1
    
    [testObject release];
    

    Block的闭包特性使得Block可以脱离其定义的作用域进行运行,所以你可以在一个函数中返回一个Block,在别的线程或者当前线程的RunLoop中进行运行,而不用担心那些引用到的外部变量是否被释放掉了。

    4.Block实际应用

    那么我们一般什么时候会用到Block呢? Blocks通常是一小段自包含的代码片段.所以它经常被用于多线程运行的代码单元(如GCD),或用于处理聚合类元素单元,或者作为某个函数调用完成后的回调函数.

    Block用作回调函数比传统的回调函数有以下的优越性:

    • 在函数调用的时候,将Block作为一个参数传给函数
    • 允许访问本地变量,这样可以避免通过结构体将本地变量封装后传递给回调函数

    应用1: Animations & Completion Handler

    [UIView animateWithDuration:2 
                   animations:^{
                        self.view.backgroundColor = [UIColor redColor];
                    }  
                    completion:^(BOOL finished){
                        if (finished){
                            self.view.backgroundColor = [UIColor blueColor];
                        } 
                    }];
    

    应用2: Enumeration
    对数据集合类中的每一个元素进行遍历,每次传入一个对象,进行处理

    NSArray *cards = [NSArray arrayWithObjects:@"Jack", @"Queen", @"King", @"Ace", nil];
    [cards enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
         NSLog(@"%@ card at index %d", object, index);
     }];
    

    应用3: Notification Handler

        
    [[NSNotificationCenter defaultCenter] 
     addObserverForName:@"TestNotification" 
     object:nil 
     queue:aNSOperationQueue 
     usingBlock:^(NSNotification *notification){
         NSLog(@"Notification: %@",notification);
     }];
    

    应用4: GCD

        
    dispatch_queue_t imageDownloadQueue = dispatch_queue_create("Image Download Queue", NULL);
    dispatch_async(imageDownloadQueue, ^{
        NSURL *imageURL = [NSURL URLWithString:@"http://xxx.xx.com/a.png"];
        NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
        UIImage *image = [UIImage imageWithData:imageData];
        dispatch_async(dispatch_get_main_queue(), ^{
            [imageView setImage:image];
        });
    });
    
  • 相关阅读:
    在Ubuntu中通过update-alternatives切换软件版本
    SCons: 替代 make 和 makefile 及 javac 的极好用的c、c++、java 构建工具
    mongodb 的使用
    利用grub从ubuntu找回windows启动项
    How to Repair GRUB2 When Ubuntu Won’t Boot
    Redis vs Mongo vs mysql
    java script 的工具
    python 的弹框
    how to use greendao in android studio
    python yield的终极解释
  • 原文地址:https://www.cnblogs.com/tianlanliao/p/4294823.html
Copyright © 2011-2022 走看看