• Block介绍(二)内存管理与其他特性


    我们在前一章介绍了block的用法,而正确使用block必须要求正确理解block的内存管理问题。这一章,我们只陈述结果而不追寻原因,我们将在下一章深入其原因。

    一、block放在哪里

    我们针对不同情况来讨论block的存放位置:

    1.栈和堆

    以下情况中的block位于堆中:

    void foo()
    {
        __block int i = 1024;
        int j = 1;
        void (^blk)(void);
        void (^blkInHeap)(void);
        blk = ^{ printf("%d, %d
    ", i, j);};//blk在栈里
        blkInHeap = Block_copy(blk);//blkInHeap在堆里
    }
     
    - (void)fooBar
    {
        _oi = 1;
        OBJ1* oj = self;
        void (^oblk)(void) = ^{ printf("%d
    ", oj.oi);};
        void (^oblkInHeap)(void) = [oblk copy];//oblkInHeap在堆中
    } 

    2.全局区

    以下情况中的block位于全局区:

    static int(^maxIntBlock)(int, int) = ^(int a, int b){return a>b?a:b;};
    - (void)fooBar
    {
         int(^maxIntBlockCopied)(int, int) =[maxIntBlock copy];
    }
     
    void foo()
    {
         int(^maxIntBlockCopied)(int, int) = Block_copy(maxIntBlock);
    } 

    需要注意的是,这里复制过后的block依旧位于全局区,实际上,复制操作是直接返回了原block对象。

    二、block引用的变量在哪里

     1.全局区

    全局区的变量存储位置与block无关:

    static int gVar = 0;
    //__block static int gMVar = 1;
     
    void foo()
    {
        static int stackVar = 0;
    //    __block static int stackMVar = 0;
    } 

    注意,static变量是不允许添加__block标记的

    2.堆栈

    此时,你可能会问,当函数foo返回后,栈上的j已经回收,那么blkInHeap怎么能继续使用它?这是因为没有__block标记的变量,会被当做实参传入block的底层实现函数中,当block中的代码被执行时,j已经不是原来的j了,所谓物是人非就是这样吧~

    另外,如果使用到变量j的所有block都没有被复制至heap,那么这个变量j也不会被复制至heap。

    因此,即使将j++这一句放到blk()这句之前,这段代码执行后,控制台打印结果也是:1024, 1。而不是1024, 2

    三、其他特性

    1.复制的行为

     对block调用复制,有以下几种情况:

    1.对全局区的block调用copy,会返回原指针,并且这期间不处理任何东西(至少目前的内部实现是这样);

    2.对栈上的block调用copy,每次会返回新复制到堆上的block的指针,同时,所有__block变量都会被复制至堆一份(多次拷贝,只会生成一份)。

    3.对已经位于heap上的block,再次调用copy,只会增加block的引用计数。

    为什么我们不讨论retian的行为?原因是并没有Block_retain()这样的函数,而且objc里面的retain消息发送给block对象后,其内部实现是什么都不做。

    2.objc类中的block复制

     objc类实例方法中的block如果被复制至heap,那么当前实例会被增加引用计数,当这个block被释放时,此实例会被减少引用计数。

    但如果这个block没有使用当前实例的任何成员,那么当前实例不会被增加引用计数。这也是很自然的道理,我既然没有用到这个instance的任何东西,那么我干嘛要retian它?

    我们要注意的一点是,我看到网上有很多人说block引起了实例与block之间的循环引用(retain-cycle),并且给出解决方案:不直接使用self而先将self赋值给一个临时变量,然后再使用这个临时变量。但是,大家注意,我们一定要为这个临时变量增加__block标记(多谢第三篇文章回帖网友的提醒)。

  • 相关阅读:
    log4j 配置文件详解
    Java 发送Get和Post请求
    java 基于百度地图API GPS经纬度解析地址
    Spring MVC 注解json 配置
    web.xml中classpath 解释
    【错误信息】springMVC No mapping found for HTTP request with URI
    栈和堆
    结构体和类的区别,联系
    Delegate,Block,Notification, KVC,KVO,Target-Action
    Protocol, Delegate
  • 原文地址:https://www.cnblogs.com/ghgyj/p/4002125.html
Copyright © 2020-2023  润新知