欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

block-对象类型的auto变量

程序员文章站 2022-04-02 21:17:22
...

从上些章节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的。
block-对象类型的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-对象类型的auto变量

总结:

当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)