美文网首页程序员
block-对象类型的auto变量

block-对象类型的auto变量

作者: RM_乾笙 | 来源:发表于2018-07-03 11:49 被阅读30次

    从上些章节block-变量的捕获(caputer)中,详细说了基本类型的auto变量的捕获,现在来了解下,对象类型的auto变量是怎样捕获和底层结构是如何的。

    block自动copy的情况

    在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

    • block作为函数返回值时
    • block赋值给__strong指针时
    • block作为Cocoa API中方法名含有usingBlock的方法参数时
    • block作为GCD API的方法参数时
    
    #import <Foundation/Foundation.h>
    #import "RMPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...        
            RMPerson *person = [[RMPerson alloc] init];
            person.age = 20;
            
            void (^block)(void) = ^ {
                NSLog(@"age is %d",person.age);
            };
    
            [person release];
            NSLog(@"-----------");
        }
        return 0;
    }
    
    ----------------- RMPerson.h -----------------
    #import <Foundation/Foundation.h>
    @interface RMPerson : NSObject
    @property (nonatomic, assign) int age;
    @end
    
    ----------------- RMPerson.m -----------------
    #import "RMPerson.h"
    @implementation RMPerson
    - (void)dealloc {
        [super delloc];
        NSLog(@"RMPerson-delloc");
    }
    @end
    
    
    // MRC 环境 控制台输出 
    2018-07-03 10:34:39.523223+0800 __block的本质[20912:1898201] RMPerson-delloc
    2018-07-03 10:36:17.021712+0800 __block的本质[20912:1898201] -----------
    Program ended with exit code: 0
    

    留意上面代码,是在MRC的环境下的代码,当NSLog(@"-----------");打印前了,RMPerson就释放了,什么原因呢?虽然block访问的是对象类型的auto变量,但还是访问了auto变量,所以block是属于NSStackBlock,是存在栈空间的,block运行完就会释放,它自己都不知道自己能存活多久,所以是不会作强持有RMPerson的操作。(结论一:如果block是在栈上,将不会对auto变量产生强引用)

    
    #import <Foundation/Foundation.h>
    #import "RMPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...        
            RMPerson *person = [[RMPerson alloc] init];
            person.age = 20;
            
            void (^block)(void) = [^ {
                NSLog(@"age is %d",person.age);
            } copy]; // copy操作,从栈中复制到堆中
    
            [person release];
            NSLog(@"-----------");
        }
        return 0;
    }
    
    ----------------- RMPerson.h -----------------
    #import <Foundation/Foundation.h>
    @interface RMPerson : NSObject
    @property (nonatomic, assign) int age;
    @end
    
    ----------------- RMPerson.m -----------------
    #import "RMPerson.h"
    @implementation RMPerson
    - (void)dealloc {
        [super delloc];
        NSLog(@"RMPerson-delloc");
    }
    @end
    
    
    // MRC 环境 控制台输出 
    2018-07-03 10:49:35.425698+0800 __block的本质[21020:1916302] -----------
    Program ended with exit code: 0
    

    从上面的控制台输出可以看出,即使程序结束了,RMPerson都没有释放,这是为什么呢? 因为block做了copy操作,从栈中拷贝到了堆中,此时block强引用了RMPerson,所以保住了RMPerson。
    下面我们来看一下,底层c++代码是如何堆中的block是如何保住RMPerson的。


    对象类型的auto变量

    从上面源码分析,

    如果block被拷贝到堆上时
    • 1.会调用block内部的copy函数
    • 2.copy函数内部会调用_Block_object_assign函数
    • 3._Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。
    如果block从堆上移除
    • 1.会调用block内部的dispose函数
    • 2.dispose函数内部会调用_Block_object_dispose函数
    • 3._Block_object_dispose函数会自动释放引用的auto变量(release)
    block copy

    总结:

    当block内部访问了对象类型的auto变量时

    1.如果block是在栈上,将不会对auto变量产生强引用

    2.如果block被拷贝到堆上

    • 会调用block内部的copy函数
    • copy函数内部会调用_Block_object_assign函数
    • _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、* __unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

    3.如果block从堆上移除

    • 会调用block内部的dispose函数
    • dispose函数内部会调用_Block_object_dispose函数
    • _Block_object_dispose函数会自动释放引用的auto变量(release)

    相关文章

      网友评论

        本文标题:block-对象类型的auto变量

        本文链接:https://www.haomeiwen.com/subject/mowluftx.html