iOS block 总结

    2013-12-20 00:00     0 条评论

简介

Block是C级别的语法和运行时特性。Block比较类似C语言的函数指针,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block。

用Apple文档的话来说,A block is an anonymous inline collection of code, and sometimes also called a "closure".

关于闭包,用阮一峰的一句话解释就是:闭包就是能够读取其它函数内部变量的函数。

这个解释用到block来也很恰当:一个函数里定义了个block,这个block可以访问该函数的内部变量。

关于block可以这样理解:
block其实包含两个部分的内容:

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

block入门

详见我之前写的一篇block入门的博客iOS Block学习

block的使用场景

那么我们一般什么时候会用到Block呢?
Blocks通常是一小段自包含的代码片段,适用作工作单元,通常用来做并发任务、遍历、以及回调。所以它经常被用于多线程运行的代码单元(如GCD),或用于处理聚合类元素单元,或者作为某个函数调用完成后的回调函数.关于更详细的block的使用场景,详见苹果官方文档

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回调

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]; }); });

block存放在内存的什么地方

出于最优性能的考虑,block最初是存储在栈(stack)上的,但是当使用Block_copy,或者copy的时候,block变量就会被拷贝到堆(heap)上。

1.栈区(NSStackBlock)

位于栈内存,函数返回后block将无效

2.堆区(NSMallocBlock)

位于堆内存

3.全局区(NSGlobalBlock)

类似函数,位于text段

下面是一个简单的打印block内存的demo:

    #import <foundation/Foundation.h>

    typedef long (^ BlockSum) (int, int);
    int main(int argc, const char * argv[])
    {

    @autoreleasepool {
        //全局区
        BlockSum block1 = ^ long (int a, int b)
        {
            return a + b;
        };
        NSLog(@"block1 = %@; the result of block1(1,2) = %ld
",block1,block1(1,2));//block1 = <__NSGlobalBlock__: 0x100001070>

        //栈区
        /**
         *  description
         *  block1和block2的区别在于,block1没有使用Block以外的任何外部变量,Block不需要建立局部变量值的快照,这使block1与函数没有任何区别,故而猜测编译器把block1放到了text代码段

         *  block1与block2唯一不同是的使用了局部变量var1,在定义(注意是定义,不是运行)block2时,局部变量var1当前值被copy到栈上,作为常量供Block使用。
         */
        int var1 = 100;
        BlockSum block2 =  ^ long (int a, int b)//block2 = <__NSStackBlock__: 0x7fff5fbff980>
        {
            return (var1 + a + b);
        };
        var1 += 100;
        NSLog(@"block2 = %@;  the result of block2(1,2) = %ld
",block2,block2(1,2));//需要主要此处打印出来的值是103而非203;局部变量var1当前值(100)被copy到栈上,作为常量供Block使用。


        //堆区
        BlockSum block3 = [block2 retain];// Block_copy(block2);//block3 = <__NSMallocBlock__: 0x100401f70>
        NSLog(@"block3 = %@; the result of block1(1,2) = %ld
",block3,block3(1,2));
        [block3 release];
        Block_release(block3);
    }
    return 0;
    }

block内存管理

虽然在Objective-C中,将Block当成对象进行处理,也可以使用对常规对象操作的retain、copy和release,但是还是尽量不要使用retain、copy、release对Block进行对象管理,而应当使用Block_copy ,Block_release()宏进行内存操作 ,以示和常规对象的区别。

其中Block_copy与copy等效,Block_release与release等效;

对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;

对于存放在不同内存区域中的block对象而言,对block的内存操作总结如下:

  1. 对NSGlobalBlock而言:retain、copy、release操作都无效;
  2. NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[[mutableAarry addObject:stackBlock],在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
  3. NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;

"__block关键字"

为了说明__block关键词,情况下面两个demo:

without__block.c文件代码:

    #include<stdio.h>
    typedef long (^ BlockSum) (int, int);
    int main()
    {
        int varTemp1 = 100;
        varTemp1 += 100;
        BlockSum sum1 = ^ long (int a, int b) {
            return varTemp1 + a + b;
        };
        varTemp1++;
        printf("result is : %ld ;
",sum1(1,2));
        return 0;
    }

with__block.c文件代码:

    #include<stdio.h>
    typedef long (^ BlockSum) (int, int);
    int main()
    {
        __block int varTemp1 = 100;
        varTemp1 += 100;
        BlockSum sum1 = ^ long (int a, int b) {
            return varTemp1 + a + b;
        };
        varTemp1++;
        printf("result is : %ld ;
",sum1(1,2));
        return 0;
    }

运行这两个demo你就会发现__block关键词的作用了。

block和self

在block中使用块外对象变量的时候,都会进行强引用,Apple官方文档描述如下

  1. If you access an instance variable by reference, a strong reference is made to self;
  2. If you access an instance variable by value, a strong reference is made to the variable.

所以假如在block中使用self的时候,尤其要注意block对self的强引用,假如self是一个UIViewController对象的时候,会导致pop ViewController的时候,ViewController不立即释放,等到下一次执行释放ViewController的时候,你会发现有很多视图元素所占的内存得不到释放,所以在ViewController对象中使用block的时候,需要注意block执行完成的时机。

参考资料

iOS中block实现的探究

正确使用Block避免Cycle Retain和Crash

Block教程系列

A Short Practical Guide to Blocks

对Objective-C中Block的追探

Clang简介

Clang致力于在编译的过程中通过自身的一套判断机制来找出代码中潜在的隐患。
使用Clang静态分析可以对以下一些情况进行分析:

  1. retain和release的正确使用
  2. 未使用的实例变量
  3. 未初始化的变量
  4. 无法到达的代码路径
  5. 引用空指针
  6. 除0
  7. 类型不兼容
  8. 缺少dealloc

更多关于clang的资料,可参阅LLVM Home

本文地址:https://www.yhawaii.net/ios-block.html
版权声明:本文为原创文章,版权归  所有,欢迎分享本文,转载请保留出处!
PREVIOUS:已经是最后一篇了

 发表评论


表情